开发中 MySQL 规范
一、建表規(guī)范
1、數(shù)據(jù)庫名、表名、字段名必須使用小寫字母或數(shù)字,并且禁止以數(shù)字開頭
示例:goods_category、agent_operate_201812_log
?
2、數(shù)據(jù)庫名、表名、字段名要做到見名識(shí)意
示例:goods_category,不能 gc
?
3、配置表建議以 xx_config 形式命名
示例:shop_payment_config
?
4、日志表建議以 xx_log 形式命名
示例:system_log
?
5、臨時(shí)表建議以 temp_xx 形式命名
示例:temp_order_info_export
?
6、創(chuàng)建時(shí)間使用 create_time,更新時(shí)間使用 update_time
類型使用 int(11) unsigned
?
7、字段類型為字符串時(shí)需要注意的
如果存儲(chǔ)的字符串長度幾乎相等,則應(yīng)該使用 char 定長字符串類型。
如果長度超過5000個(gè)字符,則應(yīng)該將字段類型定義為 text,并獨(dú)立出來一張表,用主鍵對(duì)應(yīng),避免影響其他字段的索引效率。
?
8、字段允許適當(dāng)冗余,以提高查詢性能,但必須考慮數(shù)據(jù)一致性
?
9、單表行數(shù)超過 500 萬行 或者 單表容量超過 2GB 時(shí),才推薦進(jìn)行分庫分表
?
10、當(dāng)存儲(chǔ)的字段為小數(shù)時(shí),數(shù)據(jù)類型設(shè)置為 decimal,禁止使用 float 和 double
在存儲(chǔ)的時(shí)候,float 和 double 存在精度損失的問題,很可能在比較值的時(shí)候,得到不正確的結(jié)果。
如果存儲(chǔ)的數(shù)據(jù)范圍超過 decimal 的范圍,建議將數(shù)據(jù)拆成整數(shù)和小數(shù)分開存儲(chǔ)。
存儲(chǔ)商品價(jià)格時(shí),統(tǒng)一轉(zhuǎn)為分,存儲(chǔ)類型應(yīng)為整型 int。
?
11、沒有特殊要求的情況下,所有的數(shù)據(jù)表必須使用 Innodb 存儲(chǔ)引擎
Innodb 支持事務(wù),支持行級(jí)鎖,擁有更好的并發(fā)性能和恢復(fù)性。
?
12、數(shù)據(jù)庫和數(shù)據(jù)表的字符集統(tǒng)一使用 utf8,需要存儲(chǔ) emoji 表情的使用 utf8mb4
?
13、所有數(shù)據(jù)表和字段必須寫 comment 注釋說明
有條件盡量建立數(shù)據(jù)字典。
?
14、盡量做到冷熱數(shù)據(jù)分離,減小表的寬度
表越寬,把表裝進(jìn)內(nèi)存緩沖池時(shí)所占用的內(nèi)存也就越大,也會(huì)消耗更多的 IO。
?
15、禁止在數(shù)據(jù)表中建立預(yù)留字段
預(yù)留字段的命名很難做到見名識(shí)意,并且無法選擇合適的類型,而且對(duì)預(yù)留字段修改時(shí),會(huì)對(duì)整張表進(jìn)行鎖定。
?
16、禁止在數(shù)據(jù)庫中存儲(chǔ)圖片、文件等大的二進(jìn)制數(shù)據(jù)
文件很大時(shí),IO 將會(huì)很耗時(shí),也會(huì)占用很多帶寬,影響響應(yīng)速度。
建議圖片、視頻、大文件統(tǒng)一存儲(chǔ)在文件存儲(chǔ)空間,比如阿里云、騰訊云的對(duì)象存儲(chǔ)空間和文件存儲(chǔ)空間,數(shù)據(jù)庫中只記錄文件地址。
?
17、設(shè)置合適的字符存儲(chǔ)長度
?
| 對(duì)象 ? | 年齡區(qū)間 | 類型 ? | 字節(jié) | 表示范圍 |
| 人 ? | 150歲以內(nèi) | unsigned tinyint | 1 | 無符號(hào)值 0~255 |
| 烏龜 ? | 數(shù)百歲 | unsigned smallint | 2 | 無符號(hào)值 0~65535 |
| 恐龍化石 | 數(shù)千萬年 | unsigned int | 4 | 無符號(hào)值 0~約42.9億 |
| 太陽 ? | 約50億年 | unsigned bigint | 8 | 無符號(hào)值 0~約10^19 |
?
18、條件允許,就將字符串轉(zhuǎn)換成數(shù)字類型存儲(chǔ)
比如存儲(chǔ)ip時(shí),使用 ip2long 和 long2ip
?
19、避免使用 enum 類型存儲(chǔ)字段
enum 類型的 orderby 操作效率低。
?
20、建議把所有列定義為 NOT NULL
索引 null 列需要額外的空間來保存,要占用更多空間。進(jìn)行比較時(shí)和計(jì)算時(shí)要對(duì) null 值進(jìn)行特別處理。
?
21、禁止在開發(fā)環(huán)境、測試環(huán)境直接連接生產(chǎn)環(huán)境數(shù)據(jù)庫
?二、索引規(guī)范
1、業(yè)務(wù)上具有唯一特性的字段,即使是多個(gè)字段的組合,也必須建成唯一索引
唯一索引影響 insert 的速度可以忽略不計(jì),但會(huì)明顯提高查詢速度。
另外,即使在應(yīng)用層做了非常完善的校驗(yàn)控制,只要沒有唯一索引,根據(jù)墨菲定律,必然會(huì)有臟數(shù)據(jù)產(chǎn)生。
?
2、禁止3個(gè)表以上join。需要join的字段,數(shù)據(jù)類型必須一致,當(dāng)多表關(guān)聯(lián)時(shí),保證被關(guān)聯(lián)的字段有索引
?
3、限制每張表上的索引數(shù)量,盡量不超過5個(gè)
索引增加查詢效率的同時(shí),也會(huì)降低插入和更新的效率,甚至有時(shí)會(huì)降低查詢效率。
mysql優(yōu)化器在選擇如何優(yōu)化查詢時(shí),會(huì)根據(jù)統(tǒng)一信息,對(duì)每一個(gè)可以用到的索引進(jìn)行評(píng)估,以生成一個(gè)最佳的執(zhí)行計(jì)劃。
如果同時(shí)有很多個(gè)索引都可以用于查詢,就會(huì)增加mysql優(yōu)化器生成執(zhí)行計(jì)劃的時(shí)間,進(jìn)而降低查詢性能。
?
4、在 varchar 字段上建立索引時(shí),必須指定索引長度
沒必要對(duì)全字段建立索引,根據(jù)實(shí)際文本區(qū)分度決定索引長度即可。一般對(duì)字符串?dāng)?shù)據(jù),長度為20的索引,區(qū)分度就會(huì)高達(dá) 90%。
可以使用下列語句來確定區(qū)分度。
count(distinct left (列名,索引長度)) / count(*)?
5、頁面搜索嚴(yán)禁左模糊或全模糊,如果需要,請(qǐng)使用搜索引擎解決
索引文件具有最左匹配特性,如果左邊的值未確定,則無法使用此索引。
?
6、如果有order by 的場景,請(qǐng)注意利用索引的有序性
?
7、使用延遲關(guān)聯(lián)或者子查詢優(yōu)化超多分頁場景
MySQL 并不是跳過 offset 行,而是取 offset + n 行。
當(dāng) offset 特別大時(shí),效率將會(huì)非常低,要么控制返回的總頁數(shù),要么對(duì)超過特定閥值的頁數(shù)進(jìn)行 SQL 改寫。
正例:先快速定位需要獲取的 id 段,然后再關(guān)聯(lián)。
SELECT a.* FROM 表1 a, (select id from 表1 where 條件 LIMIT 100000,20 ) b where a.id=b.id實(shí)例對(duì)比:
select a.* from agent_admin a, (select agent_admin_id from agent_admin where admin_id = 11400 limit 1000,5 ) b where a.agent_admin_id=b.agent_admin_id; 0.017sSELECT * from agent_admin where admin_id = 11400 limit 1000,5; 0.023S
?
8、建立組合索引時(shí),區(qū)分度最高的放在最左邊
?
9、哪些字段最好建索引
(1)經(jīng)常出現(xiàn)在 where 從句的字段
(2)包含在 order by,group by、distinct 中的字段
?
10、避免建立重復(fù)索引和冗余索引
建立冗余索引,ui增加查詢優(yōu)化器生成執(zhí)行計(jì)劃的時(shí)間
// 重復(fù)索引示例 primary key(id) index(id) unique key(id) // 冗余索引示例 index(a,b,c) index(a,b) index(a)
?
11、創(chuàng)建索引時(shí)盡量避免如下誤解
(1)寧濫勿缺:認(rèn)為一個(gè)查詢就需要建立一個(gè)索引
(2)寧缺毋濫:任務(wù)索引會(huì)消耗空間、嚴(yán)重拖慢更新和新增速度
(3)抵制唯一索引:認(rèn)為業(yè)務(wù)的唯一性一律需要在應(yīng)用層通過“先查后插”的方式解決
三、SQL 開發(fā)規(guī)范
?
1、不要使用 count(列名) 或 count(常量) 來替代 count(\*)
count(\*) 是 SQL92 定義的標(biāo)準(zhǔn)統(tǒng)計(jì)行數(shù)的語法,count(\*) 會(huì)統(tǒng)計(jì)值為NULL的行,而count(列名) 不會(huì)統(tǒng)計(jì)此列值為 null 的行。
2、在代碼中寫分頁查詢邏輯時(shí),如果 count 為 0 ,應(yīng)直接返回結(jié)果,避免繼續(xù)執(zhí)行后面的程序再返回結(jié)果
?
3、禁止使用存儲(chǔ)過程
存儲(chǔ)過程難以調(diào)試和擴(kuò)展,新人接手麻煩,可移植性差。
?
4、禁止使用外鍵與級(jí)聯(lián),一切外鍵概念必須在應(yīng)用層解決
以學(xué)生和成績的關(guān)系為例,學(xué)生表中的student_id是主鍵,那么成績表中的student_id則為外鍵。如果更新學(xué)生表中的 student_id,同時(shí)觸發(fā)成績表中的student_id更新,即為級(jí)聯(lián)更新。
外鍵與級(jí)聯(lián)更新適用于單機(jī)低并發(fā),不適合分布式、高并發(fā)集群;
級(jí)聯(lián)更新是強(qiáng)阻塞,存在數(shù)據(jù)庫更新風(fēng)暴的風(fēng)險(xiǎn);外鍵影響數(shù)據(jù)庫的插入速度。
?
5、建議使用預(yù)編譯語句進(jìn)行數(shù)據(jù)庫操作
盡量使用框架自帶的查詢構(gòu)造器,其底層均封裝了預(yù)編譯處理。如果特殊情況使用不了框架的查詢構(gòu)造器,也要手動(dòng)預(yù)編譯查詢。
預(yù)編譯語句可以重復(fù)使用優(yōu)化查詢器生成的執(zhí)行計(jì)劃,減少 SQL編譯 所需要的時(shí)間,還可以解決動(dòng)態(tài) SQL 所帶來的的 SQL 注入問題。
?
6、避免數(shù)據(jù)類型的隱式轉(zhuǎn)換
?
7、禁止使用 select \*,必須指定要查詢的具體字段
(1)無法使用覆蓋索引
注:覆蓋索引的含義是 select 的數(shù)據(jù)列只從索引中就能夠取得,不必讀取數(shù)據(jù)行,換句話說查詢列已經(jīng)被所建的索引覆蓋。
(2)消耗更多的 cpu 和 IO 以及網(wǎng)絡(luò)帶寬資源。
?
8、避免使用子查詢,可以把子查詢優(yōu)化成join查詢
子查詢的結(jié)果集無法使用索引,通常子查詢的結(jié)果集會(huì)被存儲(chǔ)到臨時(shí)表中,不論是內(nèi)存臨時(shí)表還是磁盤臨時(shí)表都不會(huì)存在索引,所以查詢性能會(huì)受到一定的影響。特別是對(duì)于返回結(jié)果集比較大的子查詢,其對(duì)查詢性能的影響也就越大。
注:通常子查詢?cè)趇n子句中,并且子查詢中為簡單SQL(不包含union、group by、order by、limit從句)時(shí),才可以把子查詢轉(zhuǎn)化為關(guān)聯(lián)查詢進(jìn)行優(yōu)化。
?
9、避免使用JOIN關(guān)聯(lián)太多的表
對(duì)于Mysql來說,是存在關(guān)聯(lián)緩存的,緩存的大小可以由join_buffer_size參數(shù)進(jìn)行設(shè)置。
在 Mysql 中,對(duì)于同一個(gè) SQL 多關(guān)聯(lián)一個(gè)表,就會(huì)多分配一個(gè)關(guān)聯(lián)緩存,在一個(gè)SQL 中,關(guān)聯(lián)的表越多,所占用的內(nèi)存就越大。
如果程序中大量的使用了多表關(guān)聯(lián)的操作,同時(shí) join_buffer_size 設(shè)置的也不合理的情況下,就容易造成服務(wù)器內(nèi)存溢出的情況,進(jìn)而影響到服務(wù)器數(shù)據(jù)庫性能的穩(wěn)定性。
MySQL 最多允許關(guān)聯(lián)61個(gè)表,建議不超過5個(gè)
?
10、減少和數(shù)據(jù)庫的交互操作
合并多個(gè)相同的操作到一起,可以提高處理效率。比如批量更新時(shí),將語句處理合并后,在提交到 MySQL 中進(jìn)行處理,這樣效率會(huì)更高。
一定要避免在循環(huán)中執(zhí)行 SQL。
?
11、對(duì)同一列進(jìn)行 or 判斷時(shí),使用 in 代替 or
舉例:
select user_name,age from admin where city in (1024, 1028);in 操作可以更有效的利用索引,or 大多數(shù)情況下很少能利用到索引。但需要注意的是,in 的值不要超過500個(gè)。
?
12、禁止在 SQL 語句中進(jìn)行函數(shù)轉(zhuǎn)換和計(jì)算
將數(shù)據(jù)取出來再在程序中進(jìn)行處理,比如格式化時(shí)間和轉(zhuǎn)換ip時(shí)。
?
13、在明顯不會(huì)有重復(fù)值時(shí)使用 UNINON ALL,而不是 UNION
UNION 會(huì)把兩個(gè)結(jié)果集的所有數(shù)據(jù)放到臨時(shí)表,再進(jìn)行去重操作
UNINON ALL不會(huì)再對(duì)結(jié)果集進(jìn)行去重操作
?
14、拆分復(fù)雜的大 SQL 為多個(gè)小 SQL
SQL 拆分后可以通過并行執(zhí)行來提高處理效率。
?
15、大批量操作分批執(zhí)行
大批量修改數(shù)據(jù),會(huì)造成表中大量數(shù)據(jù)行被鎖定,從而造成大量的阻塞。
長時(shí)間的阻塞會(huì)占滿數(shù)據(jù)庫所有的可用連接,使生產(chǎn)環(huán)境中的其他應(yīng)用無法連接到數(shù)據(jù)庫。
因此一定要注意大批量寫操作一定要分批執(zhí)行。
?
原文鏈接:https://www.haveyb.com/article/149
總結(jié)
以上是生活随笔為你收集整理的开发中 MySQL 规范的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: MySQL - 锁
- 下一篇: mysql中局部变量说法正确的是_mys