MySQL數(shù)據(jù)庫
(一)建表規(guī)約
1.【強(qiáng)制】表達(dá)是與否概念的字段究反,必須使用is_xxx的方式命名,數(shù)據(jù)類型是unsignedtinyint(1表示是儒洛,0表示否)
精耐。說明:任何字段如果為非負(fù)數(shù),必須是unsigned晶丘。正例:表達(dá)邏輯刪除的字段名is_deleted黍氮,1表示刪除,0表示未刪除浅浮。
2.【強(qiáng)制】表名、字段名必須使用小寫字母或數(shù)字捷枯,禁止出現(xiàn)數(shù)字開頭滚秩,禁止兩個下劃線中間只出現(xiàn)數(shù)字。
數(shù)據(jù)庫字段名的修改代價(jià)很大淮捆,因?yàn)闊o法進(jìn)行預(yù)發(fā)布郁油,所以字段名稱需要慎重考慮。
說明:MySQL在Windows下不區(qū)分大小寫攀痊,但在Linux下默認(rèn)是區(qū)分大小寫桐腌。
因此,數(shù)據(jù)庫名苟径、表名案站、字段名,都不允許出現(xiàn)任何大寫字母棘街,避免節(jié)外生枝蟆盐。
正例:aliyun_admin,rdc_config遭殉,level3_name
反例:AliyunAdmin石挂,rdcConfig,level_3_name
3.【強(qiáng)制】表名不使用復(fù)數(shù)名詞险污。說明:表名應(yīng)該僅僅表示表里面的實(shí)體內(nèi)容痹愚,不應(yīng)該表示實(shí)體數(shù)量,對應(yīng)于DO類名也是單數(shù)形式蛔糯,符合表達(dá)習(xí)慣拯腮。
4.【強(qiáng)制】禁用保留字,如desc渤闷、range疾瓮、match、delayed等飒箭,請參考MySQL官方保留字狼电。
5.【強(qiáng)制】主鍵索引名為pk_字段名蜒灰;唯一索引名為uk_字段名;普通索引名則為idx_字段名肩碟。
說明:pk_ 即primary key强窖;uk_ 即uniquekey;idx_ 即index的簡稱削祈。
6.【強(qiáng)制】小數(shù)類型為decimal翅溺,禁止使用float和double。
說明:float和double在存儲的時候髓抑,存在精度損失的問題咙崎,很可能在值的比較時,得到不正確的結(jié)果吨拍。
如果存儲的數(shù)據(jù)范圍超過decimal的范圍褪猛,建議將數(shù)據(jù)拆成整數(shù)和小數(shù)分開存儲。
7.【強(qiáng)制】如果存儲的字符串長度幾乎相等羹饰,使用char定長字符串類型伊滋。
8.【強(qiáng)制】varchar是可變長字符串,不預(yù)先分配存儲空間队秩,長度不要超過5000笑旺,如果存儲長度大于此值,定義字段類型為text馍资,獨(dú)立出來一張表筒主,用主鍵來對應(yīng),避免影響其它字段索引效率迷帜。
9.【強(qiáng)制】表必備三字段:id, gmt_create, gmt_modified物舒。
說明:其中id必為主鍵,類型為unsigned bigint戏锹、單表時自增冠胯、步長為1。
gmt_create, gmt_modified的類型均為date_time類型锦针,前者現(xiàn)在時表示主動創(chuàng)建荠察,后者過去分詞表示被動更新。
10.【推薦】表的命名最好是加上“業(yè)務(wù)名稱_表的作用”奈搜。正例:alipay_task/ force_project/ trade_config
11.【推薦】庫名與應(yīng)用名稱盡量一致悉盆。
12.【推薦】如果修改字段含義或?qū)ψ侄伪硎镜臓顟B(tài)追加時,需要及時更新字段注釋馋吗。
13.【推薦】字段允許適當(dāng)冗余焕盟,以提高查詢性能,但必須考慮數(shù)據(jù)一致宏粤。
冗余字段應(yīng)遵循:
1)不是頻繁修改的字段脚翘。
2)不是varchar超長字段灼卢,更不能是text字段。
正例:商品類目名稱使用頻率高来农,字段長度短鞋真,名稱基本一成不變,可在相關(guān)聯(lián)的表中冗余存儲類目名稱沃于,避免關(guān)聯(lián)查詢涩咖。
14.【推薦】單表行數(shù)超過500萬行或者單表容量超過2GB,才推薦進(jìn)行分庫分表繁莹。
說明:如果預(yù)計(jì)三年后的數(shù)據(jù)量根本達(dá)不到這個級別檩互,請不要在創(chuàng)建表時就分庫分表。
15.【參考】合適的字符存儲長度咨演,不但節(jié)約數(shù)據(jù)庫表空間盾似、節(jié)約索引存儲,更重要的是提升檢索速度雪标。
正例:如下表,其中無符號值可以避免誤存負(fù)數(shù)溉跃,且擴(kuò)大了表示范圍村刨。
對象年齡區(qū)間類型字節(jié)表示范圍
人150歲之內(nèi)unsigned tinyint1無符號值:0到255
龜數(shù)百歲unsigned smallint2無符號值:0到65535
恐龍化石數(shù)千萬年unsigned int4無符號值:0到約42.9億
太陽約50億年unsigned bigint8無符號值:0到約10的19次方
(二)索引規(guī)約
1.【強(qiáng)制】業(yè)務(wù)上具有唯一特性的字段,即使是多個字段的組合撰茎,也必須建成唯一索引嵌牺。
說明:不要以為唯一索引影響了insert速度,這個速度損耗可以忽略龄糊,但提高查找速度是明顯的逆粹;
另外,即使在應(yīng)用層做了非常完善的校驗(yàn)控制炫惩,只要沒有唯一索引僻弹,根據(jù)墨菲定律,必然有臟數(shù)據(jù)產(chǎn)生他嚷。
2.【強(qiáng)制】超過三個表禁止join蹋绽。需要join的字段,數(shù)據(jù)類型必須絕對一致筋蓖;
多表關(guān)聯(lián)查詢時卸耘,保證被關(guān)聯(lián)的字段需要有索引。說明:即使雙表join也要注意表索引粘咖、SQL性能蚣抗。
3.【強(qiáng)制】在varchar字段上建立索引時,必須指定索引長度瓮下,沒必要對全字段建立索引翰铡,根據(jù)實(shí)際文本區(qū)分度決定索引長度即可钝域。
說明:索引的長度與區(qū)分度是一對矛盾體,一般對字符串類型數(shù)據(jù)两蟀,長度為20的索引网梢,區(qū)分度會高達(dá)90%以上,可以使用count(distinctleft(列名, 索引長度))/count()的區(qū)分度來確定赂毯。
4.【強(qiáng)制】頁面搜索嚴(yán)禁左模糊或者全模糊战虏,如果需要請走搜索引擎來解決。
說明:索引文件具有B-Tree的最左前綴匹配特性党涕,如果左邊的值未確定烦感,那么無法使用此索引。
5.【推薦】如果有orderby的場景膛堤,請注意利用索引的有序性手趣。
orderby最后的字段是組合索引的一部分,并且放在索引組合順序的最后肥荔,避免出現(xiàn)file_sort的情況绿渣,影響查詢性能。
正例:wherea=? andb=? orderbyc;索引:a_b_c
反例:索引中有范圍查找燕耿,那么索引有序性無法利用中符,如:WHEREa>10 ORDERBYb;索引a_b無法排序。
6.【推薦】利用覆蓋索引來進(jìn)行查詢操作誉帅,避免回表淀散。
說明:如果一本書需要知道第11章是什么標(biāo)題,會翻開第11章對應(yīng)的那一頁嗎蚜锨?目錄瀏覽一下就好档插,這個目錄就是起到覆蓋索引的作用。
正例:能夠建立索引的種類:主鍵索引亚再、唯一索引郭膛、普通索引,而覆蓋索引是一種查詢的一種效果针余,用explain的結(jié)果饲鄙,extra列會出現(xiàn):usingindex。
7.【推薦】利用延遲關(guān)聯(lián)或者子查詢優(yōu)化超多分頁場景圆雁。
說明:MySQL并不是跳過offset行忍级,而是取offset+N行,然后返回放棄前offset行伪朽,返回N行轴咱,
那當(dāng)offset特別大的時候,效率就非常的低下,要么控制返回的總頁數(shù)朴肺,要么對超過特定閾值的頁數(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
8.【推薦】SQL性能優(yōu)化的目標(biāo):至少要達(dá)到range級別戈稿,要求是ref級別西土,如果可以是consts最好。
說明:
1)consts單表中最多只有一個匹配行(主鍵或者唯一索引)鞍盗,在優(yōu)化階段即可讀取到數(shù)據(jù)需了。
2)ref指的是使用普通的索引(normalindex)。
3)range對索引進(jìn)行范圍檢索般甲。
反例:explain表的結(jié)果肋乍,type=index,索引物理文件全掃描敷存,速度非常慢墓造,這個index級別比較range還低,與全表掃描是小巫見大巫锚烦。
9.【推薦】建組合索引的時候觅闽,區(qū)分度最高的在最左邊。正例:如果wherea=? andb=? 涮俄,a列的幾乎接近于唯一值谱煤,那么只需要單建idx_a索引即可。
說明:存在非等號和等號混合判斷條件時禽拔,在建索引時,請把等號條件的列前置室叉。
如:wherea>? andb=? 那么即使a的區(qū)分度更高睹栖,也必須把b放在索引的最前列。
10.【推薦】防止因字段類型不同造成的隱式轉(zhuǎn)換茧痕,導(dǎo)致索引失效野来。
11.【參考】創(chuàng)建索引時避免有如下極端誤解:
1)寧濫勿缺。認(rèn)為一個查詢就需要建一個索引踪旷。
2)寧缺勿濫曼氛。認(rèn)為索引會消耗空間、嚴(yán)重拖慢更新和新增速度令野。
3)抵制惟一索引舀患。認(rèn)為業(yè)務(wù)的惟一性一律需要在應(yīng)用層通過“先查后插”方式解決。
(三)SQL語句
1.【強(qiáng)制】不要使用count(列名)或count(常量)來替代count()气破,count()是SQL92定義的標(biāo)準(zhǔn)統(tǒng)計(jì)行數(shù)的語法聊浅,跟數(shù)據(jù)庫無關(guān),跟NULL和非NULL無關(guān)。
說明:count(*)會統(tǒng)計(jì)值為NULL的行低匙,而count(列名)不會統(tǒng)計(jì)此列為NULL值的行旷痕。
2.【強(qiáng)制】count(distinctcol)計(jì)算該列除NULL之外的不重復(fù)行數(shù),注意count(distinctcol1, col2)如果其中一列全為NULL顽冶,那么即使另一列有不同的值欺抗,也返回為0。
3.【強(qiáng)制】當(dāng)某一列的值全是NULL時强重,count(col)的返回結(jié)果為0绞呈,但sum(col)的返回結(jié)果為NULL,因此使用sum()時需注意NPE問題竿屹。
正例:可以使用如下方式來避免sum的NPE問題:SELECTIF(ISNULL(SUM(g)),0,SUM(g))FROMtable;
4.【強(qiáng)制】使用ISNULL()來判斷是否為NULL值报强。
說明:NULL與任何值的直接比較都為NULL。
1)NULL<>NULL的返回結(jié)果是NULL拱燃,而不是false秉溉。
2)NULL=NULL的返回結(jié)果是NULL,而不是true碗誉。
3)NULL<>1的返回結(jié)果是NULL召嘶,而不是true。
5.【強(qiáng)制】在代碼中寫分頁查詢邏輯時哮缺,若count為0應(yīng)直接返回弄跌,避免執(zhí)行后面的分頁語句。
6.【強(qiáng)制】不得使用外鍵與級聯(lián)尝苇,一切外鍵概念必須在應(yīng)用層解決铛只。
說明:以學(xué)生和成績的關(guān)系為例,學(xué)生表中的student_id是主鍵糠溜,那么成績表中的student_id則為外鍵淳玩。
如果更新學(xué)生表中的student_id,同時觸發(fā)成績表中的student_id更新非竿,即為級聯(lián)更新蜕着。
外鍵與級聯(lián)更新適用于單機(jī)低并發(fā),不適合分布式红柱、高并發(fā)集群承匣;
級聯(lián)更新是強(qiáng)阻塞,存在數(shù)據(jù)庫更新風(fēng)暴的風(fēng)險(xiǎn)锤悄;外鍵影響數(shù)據(jù)庫的插入速度韧骗。
7.【強(qiáng)制】禁止使用存儲過程,存儲過程難以調(diào)試和擴(kuò)展零聚,更沒有移植性宽闲。
8.【強(qiáng)制】數(shù)據(jù)訂正時众眨,刪除和修改記錄時,要先select容诬,避免出現(xiàn)誤刪除娩梨,確認(rèn)無誤才能執(zhí)行更新語句。
9.【推薦】in操作能避免則避免览徒,若實(shí)在避免不了狈定,需要仔細(xì)評估in后邊的集合元素?cái)?shù)量,控制在1000個之內(nèi)习蓬。
10.【參考】如果有全球化需要纽什,所有的字符存儲與表示,均以utf-8編碼躲叼,注意字符統(tǒng)計(jì)函數(shù)的區(qū)別芦缰。
說明:
SELECT LENGTH(“輕松工作”);返回為12
SELECT CHARACTER_LENGTH(“輕松工作”)枫慷;返回為4
如果需要存儲表情让蕾,那么選擇utfmb4來進(jìn)行存儲,注意它與utf-8編碼的區(qū)別或听。
11.【參考】TRUNCATETABLE比DELETE速度快探孝,且使用的系統(tǒng)和事務(wù)日志資源少,
但TRUNCATE無事務(wù)且不觸發(fā)trigger誉裆,有可能造成事故顿颅,故不建議在開發(fā)代碼中使用此語句。
說明:
TRUNCATETABLE在功能上與不帶WHERE子句的DELETE語句相同足丢。
(四)ORM映射
1.【強(qiáng)制】在表查詢中粱腻,一律不要使用* 作為查詢的字段列表,需要哪些字段必須明確寫明斩跌。
說明:
1)增加查詢分析器解析成本栖疑。
2)增減字段容易與resultMap配置不一致。
2.【強(qiáng)制】POJO類的布爾屬性不能加is滔驶,而數(shù)據(jù)庫字段必須加is_,要求在resultMap中進(jìn)行字段與屬性之間的映射卿闹。
說明:參見定義POJO類以及數(shù)據(jù)庫字段定義規(guī)定揭糕,在<resultMap>中增加映射,是必須的锻霎。
在MyBatis Generator生成的代碼中著角,需要進(jìn)行對應(yīng)的修改。
3.【強(qiáng)制】不要用resultClass當(dāng)返回參數(shù)旋恼,即使所有類屬性名與數(shù)據(jù)庫字段一一對應(yīng)吏口,也需要定義;
反過來,每一個表也必然有一個與之對應(yīng)产徊。說明:配置映射關(guān)系昂勒,使字段與DO類解耦,方便維護(hù)舟铜。
4.【強(qiáng)制】sql.xml配置參數(shù)使用:#{}戈盈,#param# 不要使用${} 此種方式容易出現(xiàn)SQL注入。
5.【強(qiáng)制】iBATIS自帶的queryForList(StringstatementName,intstart,intsize)不推薦使用谆刨。
說明:其實(shí)現(xiàn)方式是在數(shù)據(jù)庫取到statementName對應(yīng)的SQL語句的所有記錄塘娶,再通過subList取start,size的子集合。
正例:Map<String, Object> map = new HashMap<String, Object>(); map.put(“start”, start); map.put(“size”, size);
6.【強(qiáng)制】不允許直接拿HashMap與Hashtable作為查詢結(jié)果集的輸出痊夭。
說明:resultClass=”Hashtable”刁岸,會置入字段名和屬性值,但是值的類型不可控她我。
7.【強(qiáng)制】更新數(shù)據(jù)表記錄時虹曙,必須同時更新記錄對應(yīng)的gmt_modified字段值為當(dāng)前時間。
8.【推薦】不要寫一個大而全的數(shù)據(jù)更新接口鸦难。
傳入為POJO類根吁,不管是不是自己的目標(biāo)更新字段,都進(jìn)行update table set c1=value1,c2=value2,c3=value3;
這是不對的合蔽。執(zhí)行SQL時击敌,不要更新無改動的字段,一是易出錯拴事;二是效率低沃斤;三是增加binlog存儲。
9.【參考】@Transactional事務(wù)不要濫用刃宵。事務(wù)會影響數(shù)據(jù)庫的QPS衡瓶,另外使用事務(wù)的地方需要考慮各方面的回滾方案,包括緩存回滾牲证、搜索引擎回滾哮针、消息補(bǔ)償、統(tǒng)計(jì)修正等坦袍。
10.【參考】<isEqual>中的compareValue是與屬性值對比的常量十厢,一般是數(shù)字,表示相等時帶上此條件捂齐;
<isNotEmpty>表示不為空且不為null時執(zhí)行蛮放;<isNotNull>表示不為null值時執(zhí)行