阿里手冊是阿里工程師多年一線經驗的結晶,遵循其中的約定與規範,能很大程度的減小某些未知的隱患。linux
其規約強度由強到弱分爲強制、推薦、參考三大類。sql
本文詳細解讀了阿里手冊的MySQL部分,若是是一些很明確的、不須要過多解釋的約定,本文不會解讀。數據庫
本文分爲四部分:緩存
【強制】表達是與否概念的字段,必須使用 is_xxx 的方式命名,數據類型是 unsigned tinyint (1 表示是,0 表示否)。性能優化
【強制】表名、字段名必須使用小寫字母或數字,禁止出現數字開頭,禁止兩個下劃線中間只 出現數字。數據庫字段名的修改代價很大,由於沒法進行預發佈,因此字段名稱須要慎重考慮。微信
MySQL在Linux下數據庫名、表名、列名、別名大小寫規則:網絡
(1)數據庫名與表名是嚴格區分大小寫併發
(2)表的別名是嚴格區分大小寫分佈式
(3)列名與列的別名在全部的狀況下均是忽略大小寫的函數
(4)變量名也是嚴格區分大小寫的
【強制】表名不使用複數名詞。
【強制】禁用保留字,如 desc、range、match、delayed 等,請參考 MySQL 官方保留字。
【強制】主鍵索引名爲 pk_字段名;惟一索引名爲 uk_字段名;普通索引名則爲 idx_字段名。
【強制】小數類型爲 decimal,禁止使用 float 和 double。
FLOAT和DOUBLE在不指定精度時,默認會按照實際的精度來顯示,而DECIMAL在不指定精度時,默認整數爲10,小數爲0。因此建議在定義表時,定義(M,D)。
float和double在設置超過定義長度的數值時,會自動四捨五入,decimal會截斷,並給出一條警告。
精度損失問題:float和double類型的列,在作sum計算時,會丟失精度,而decimal會精確計算。
【強制】若是存儲的字符串長度幾乎相等,使用 char 定長字符串類型。
【強制】varchar 是可變長字符串,不預先分配存儲空間,長度不要超過 5000,若是存儲長 度大於此值,定義字段類型爲 text,獨立出來一張表,用主鍵來對應,避免影響其它字段索 引效率。
咱們一般編碼設置爲U8,每一個字符最多佔3個字節,那麼最大長度不能超過21845。
若定義的時候超過上述限制,則varchar字段會被強行轉爲text類型,併產生warning。
此外,受MYSQL行長度限制影響,MySQL要求一個行的定義長度不能超過65535。若定義的表長度超過這個值,則提示ERROR 1118 (42000): Row size too large。
數據庫中定義的varchar(20)指的是20個字符。
關於5000的建議:因爲一般定義的U8每一個字符佔3個字節,那麼5000字符須要15000個字節,考慮行最大長度限制和別的列,以及查詢性能,推薦5000。
【強制】表必備三字段:id, gmt_create, gmt_modified。
【推薦】表的命名最好是加上「業務名稱_表的做用」。
【推薦】庫名與應用名稱儘可能一致。
【推薦】若是修改字段含義或對字段表示的狀態追加時,須要及時更新字段註釋。
【推薦】字段容許適當冗餘,以提升查詢性能,但必須考慮數據一致。冗餘字段應遵循:
1) 不是頻繁修改的字段。
2) 不是 varchar 超長字段,更不能是 text 字段。
【推薦】單錶行數超過 500 萬行或者單表容量超過 2GB,才推薦進行分庫分表。
說明: 若是預計三年後的數據量根本達不到這個級別,請不要在建立表時就分庫分表。
解讀:超事後對各方面性能影響較大,淘新聞出現過一次表過大引起的故障。
【參考】合適的字符存儲長度,不但節約數據庫表空間、節約索引存儲,更重要的是提高檢索速度。
類型 | 年齡 | 字段類型 | 字段長度 | 取值範圍 |
---|---|---|---|---|
人 | 150歲以內 | tinyint unsigned | 1 | 無符號值: 0~255 |
【強制】業務上具備惟一特性的字段,即便是多個字段的組合,也必須建成惟一索引。
【強制】超過三個表禁止 join。須要 join 的字段,數據類型必須絕對一致; 多表關聯查詢時,保證被關聯的字段須要有索引。
【強制】在 varchar 字段上創建索引時,必須指定索引長度,不必對全字段創建索引,根據實際文本區分度決定索引長度便可。
對於blob,text,varchar的列必須使用前綴索引,MySQL不容許索引這些列的完整長度。
最好選擇足夠長的前綴保證較高的區分度,也不能太長(節省空間)。
【強制】頁面搜索嚴禁左模糊或者全模糊,若是須要請走搜索引擎來解決。
說明: 索引文件具備 B-Tree 的最左前綴匹配特性,若是左邊的值未肯定,那麼沒法使用此索引。
【推薦】若是有 order by 的場景,請注意利用索引的有序性。 order by 最後的字段是組合索引的一部分,而且放在索引組合順序的最後,避免出現 file_sort 的狀況,影響查詢性能。
【推薦】利用覆蓋索引來進行查詢操做, 避免回表。
因爲覆蓋索引必需要存儲索引列的值,哈希索引、空間索引和全文索引都不存儲列的值,MySQL只有B-Tree索引能夠作覆蓋索引。如:對id,name,title三個字段創建索引,在索引中會存儲這三個列的值,若是查詢:select id,name,title from table where id < 10; 經過explain會看到extra爲using index。
若是查詢select * from table where id < 10;就不會使用覆蓋索引,由於索引中沒有包含全部的列值。
【推薦】利用延遲關聯或者子查詢優化超多分頁場景。
select count(*) from user_game_info; // 共有956176條數據
select * from user_game_info a limit 900000, 20; // 此查詢耗時0.547S
select t1.* from user_game_info t1, (select id from user_game_info limit 900000, 20) t2 where t1.id = t2.id; // 優化後耗時0.178S
【推薦】 SQL 性能優化的目標:至少要達到 range 級別, 要求是 ref 級別, 若是能夠是 consts最好。
1) consts 單表中最多隻有一個匹配行( 主鍵或者惟一索引) ,在優化階段便可讀取到數據。
2) ref 指的是使用普通的索引( normal index) 。
3) range 對索引進行範圍檢索。
反例: explain 表的結果, type=index,索引物理文件全掃描,速度很是慢,這個 index 級別比較 range 還低,與全表掃描是小巫見大巫。
解讀:這裏說的是explain中的type字段(鏈接類型)。
常見的幾種類型有:all、index、range、ref、eq_ref、const,從左到右效率依次加強。
1)All:全表掃描
2)Index:先掃描所有索引,再回表獲取數據,性能不比all強
3)Range:有範圍的索引掃描,between/and/>/</in/or可觸發
4)Ref:查找條件列使用了索引並且不爲主鍵和unique
5)Eq_ref:優化器已知查詢結果只有一個,在使用了主鍵或惟一索引的狀況下觸發
6)Const:將主鍵放到where後面作等值查詢,例如:select * from user_game_info where id = 100;
【推薦】建組合索引的時候,區分度最高的在最左邊。
【推薦】 防止因字段類型不一樣形成的隱式轉換,致使索引失效。
以字符串形式查找,命中索引
因隱式轉換,未命中索引
隱式轉換規則:
【參考】建立索引時避免有以下極端誤解:
【強制】不要使用 count(列名)或 count(常量)來替代 count(), count()是 SQL92 定義的標準統計行數的語法,跟數據庫無關,跟 NULL 和非 NULL 無關。
【強制】 count(distinct col) 計算該列除 NULL 以外的不重複行數, 注意 count(distinctcol1, col2) 若是其中一列全爲 NULL,那麼即便另外一列有不一樣的值,也返回爲 0。
【強制】當某一列的值全是 NULL 時, count(col)的返回結果爲 0,但 sum(col)的返回結果爲NULL,所以使用 sum()時需注意 NPE 問題。
【強制】使用 ISNULL()來判斷是否爲 NULL 值。
1) NULL<>NULL 的返回結果是 NULL, 而不是 false。
2) NULL=NULL 的返回結果是 NULL, 而不是 true。
3) NULL<>1 的返回結果是 NULL,而不是 true。
【強制】 在代碼中寫分頁查詢邏輯時,若 count 爲 0 應直接返回,避免執行後面的分頁語句。
【強制】不得使用外鍵與級聯,一切外鍵概念必須在應用層解決。
【強制】禁止使用存儲過程,存儲過程難以調試和擴展,更沒有移植性。
【強制】數據訂正(特別是刪除、 修改記錄操做) 時,要先 select,避免出現誤刪除,確認無誤才能執行更新語句。
【推薦】 in 操做能避免則避免,若實在避免不了,須要仔細評估 in 後邊的集合元素數量,控制在 1000 個以內。
【參考】 若是有國際化須要,全部的字符存儲與表示,均以 utf-8 編碼,注意字符統計函數的區別。
Emoji表情不在utf8的3個字節的表示範圍以內,能夠用utf8mb4存儲。
【參考】 TRUNCATE TABLE 比 DELETE 速度快,且使用的系統和事務日誌資源少,但 TRUNCATE無事務且不觸發 trigger,有可能形成事故,故不建議在開發代碼中使用此語句。說明: TRUNCATE TABLE 在功能上與不帶 WHERE 子句的 DELETE 語句相同。
【強制】在表查詢中,一概不要使用 * 做爲查詢的字段列表,須要哪些字段必須明確寫明。
1) 增長查詢分析器解析成本。
2) 增減字段容易與 resultMap 配置不一致。
3)無用字段增長網絡消耗,尤爲是 text 類型的字段。
【強制】 POJO 類的布爾屬性不能加 is,而數據庫字段必須加 is_,要求在 resultMap 中進行字段與屬性之間的映射。
【強制】不要用 resultClass 當返回參數,即便全部類屬性名與數據庫字段一一對應,也須要定義; 反過來,每個表也必然有一個 POJO 類與之對應。
【強制】sql.xml 配置參數使用: #{}, #param# 不要使用${} 此種方式容易出現 SQL 注入。
select * from user where name = ?;
而 ${} 則只是簡單的字符串替換,在動態解析階段,該 sql 語句會被解析成
select * from user where name = 'zhangsan’;
以上,#{} 的參數替換是發生在 DBMS 中,而 ${} 則發生在動態解析過程當中。
【強制】 iBATIS 自帶的 queryForList(String statementName,int start,int size)不推薦使用。
【強制】不容許直接拿 HashMap 與 Hashtable 做爲查詢結果集的輸出。
【推薦】不要寫一個大而全的數據更新接口。 傳入爲 POJO 類,無論是否是本身的目標更新字段,都進行 update table set c1=value1,c2=value2,c3=value3; 這是不對的。執行 SQL時, 不要更新無改動的字段,一是易出錯; 二是效率低; 三是增長 binlog 存儲。
【參考】 @Transactional 事務不要濫用。事務會影響數據庫的 QPS,另外使用事務的地方須要考慮各方面的回滾方案,包括緩存回滾、搜索引擎回滾、消息補償、統計修正等。
【參考】 中的 compareValue 是與屬性值對比的常量,通常是數字,表示相等時帶上此條件; 表示不爲空且不爲 null 時執行; 表示不爲 null 值時執行。