Java開發者必需要知道的MySQL規範

規範在整個後端執行也有大半年的時間,對於整個團隊在開發階段就減小不恰當的建表語句、錯誤 SQL、錯誤的索引有積極的意義,故分享出來給你們參考。java


下邊分爲建表規約、SQL 規約、索引規約三個部分,每部分的每一條都有強制、建議兩個級別,你們在參考時,根據本身公司的狀況來權衡。數據庫


建表規約後端


【強制】存儲引擎必須使用 InnoDB緩存


解讀:InnoDB 支持事物、行級鎖、併發性能更好,CPU 及內存緩存頁優化使得資源利用率更高。服務器


【強制】:②每張表必須設置一個主鍵 ID,且這個主鍵 ID 使用自增主鍵(在知足須要的狀況下儘可能短),除非在分庫分表環境下微信


解讀:因爲 InnoDB 組織數據的方式決定了須要有一個主鍵,並且如果這個主鍵 ID 是單調遞增的能夠有效提升插入的性能,避免過多的頁分裂、減小表碎片提升空間的使用率。
併發


而在分庫分表環境下,則須要統一來分配各個表中的主鍵值,從而避免整個邏輯表中主鍵重複。app


【強制】:③必須使用 utf8mb4 字符集less


解讀:在 MySQL 中的 UTF-8 並不是「真正的 UTF-8」,而 utf8mb4」纔是真正的「UTF-8」。編輯器


【強制】:④數據庫表、表字段必須加入中文註釋


解讀:你們都別懶。


【強制】:⑤庫名、表名、字段名均小寫,下劃線風格,不超過 32 個字符,必須見名知意,禁止拼音英文混用


解讀:約定。


【強制】:⑥單表列數目必須小於 30,若超過則應該考慮將表拆分


解讀:單表列數太多使得 MySQL 服務器處理 InnoDB 返回數據之間的映射成本過高。


【強制】:⑦禁止使用外鍵,若是有外鍵完整性約束,須要應用程序控制


解讀:外鍵會致使表與表之間耦合,UPDATE 與 DELETE 操做都會涉及相關聯的表,十分影響 SQL 的性能,甚至會形成死鎖。


【強制】:⑧必須把字段定義爲 NOT NULL 而且提供默認值


解讀:

  • NULL 的列使索引/索引統計/值比較都更加複雜,對 MySQL 來講更難優化。

  • NULL 這種類型 MySQL 內部須要進行特殊處理,增長數據庫處理記錄的複雜性;同等條件下,表中有較多空字段的時候,數據庫的處理性能會下降不少。

  • NULL 值須要更多的存儲空,不管是表仍是索引中每行中的 NULL 的列都須要額外的空間來標識。


【強制】:⑨禁用保留字,如 DESC、RANGE、MARCH 等


解讀:請參考 MySQL 官方保留字。


【強制】:⑩若是存儲的字符串長度幾乎相等,使用 CHAR 定長字符串類型


解讀:可以減小空間碎片,節省存儲空間。


【建議】: 在一些場景下,考慮使用 TIMESTAMP 代替 DATETIME


解讀:
  • 這兩種類型的都能表達"yyyy-MM-dd HH:mm:ss"格式的時間,TIMESTAMP 只須要佔用 4 個字節的長度,能夠存儲的範圍爲(1970-2038)年,在各個時區,所展現的時間是不同的。

  • 而 DATETIME 類型佔用 8 個字節,對時區不敏感,能夠存儲的範圍爲(1001-9999)年。


【建議】:⑫小心自動生成的 Schema,建議全部的 Schema 手動編寫


解讀: 對於一些數據庫客戶端不要太過信任。

SQL 規約


【建議】:①爲了充分利用緩存,不容許使用自定義函數、存儲函數、用戶變量


解讀:若是查詢中包含任何用戶自定義函數、存儲函數、用戶變量、臨時表、MySQL 庫中的系統表,其查詢結果都不會被緩存。


好比函數 NOW() 或者 CURRENT_DATE() 會由於不一樣的查詢時間,返回不一樣的查詢結果。


【強制】:②在查詢中指定所需的列,而不是直接使用「 *」返回全部的列

解讀:
  • 讀取不須要的列會增長 CPU、IO、NET 消耗。

  • 不能有效的利用覆蓋索引。


【強制】:③不容許使用屬性隱式轉換

解讀:假設咱們在手機號列上添加了索引,而後執行下面的 SQL 會發生什麼?


explain SELECT user_name FROM parent WHERE phone=13812345678;很明顯就是索引不生效,會全表掃描。


【建議】:④在 WHERE 條件的屬性上使用函數或者表達式


解讀: MySQL 沒法自動解析這種表達式,沒法使用到索引。

【強制】: 禁止使用外鍵與級聯,一切外鍵概念必須在應用層解決


解讀: 外鍵與級聯更新適用於單機低併發,不適合分佈式、高併發集羣;級聯更新是強阻塞,存在數據庫更新風暴的風險;外鍵影響數據庫的插入速度。


【建議】:⑥應儘可能避免在 WHERE 子句中使用 or 做爲鏈接條件


解讀: 根據狀況能夠選擇使用 UNION ALL 來代替 OR。

【強制】:⑦不容許使用 % 開頭的模糊查詢


解讀: 根據索引的最左前綴原理,%開頭的模糊查詢沒法使用索引,可使用 ES 來作檢索。


索引規約


【建議】:①避免在更新比較頻繁、區分度不高的列上單獨創建索引


解讀: 區分度不高的列單首創建索引的優化效果很小,可是較爲頻繁的更新則會讓索引的維護成本更高。

【強制】:②JOIN 的表不容許超過五個。須要 JOIN 的字段,數據類型必須絕對一致; 多表關聯查詢時,保證被關聯的字段須要有索引


解讀: 太多表的 JOIN 會讓 MySQL 的優化器更難權衡出一個「最佳」的執行計劃(可能性爲表數量的階乘),同時要注意關聯字段的類型、長度、字符編碼等等是否一致。

【強制】:③在一個聯合索引中,若第一列索引區分度等於 1,那麼則不須要創建聯合索引


解讀: 索引經過第一列就可以徹底定位的數據,因此聯合索引的後邊部分是不須要的。

【強制】:④創建聯合索引時,必須將區分度更高的字段放在左邊


解讀: 區分度更高的列放在左邊,可以在一開始就有效的過濾掉無用數據。提升索引的效率,相應咱們在 Mapper 中編寫 SQL 的 WHERE 條件中有多個條件時,須要先看看當前表是否有現成的聯合索引直接使用,注意各個條件的順序儘可能和索引的順序一致。

【建議】:⑤利用覆蓋索引來進行查詢操做,避免回表

解讀:覆蓋查詢便是查詢只須要經過索引便可拿到所需 DATA,而再也不須要再次回表查詢,因此效率相對很高。


咱們在使用 EXPLAIN 的結果,extra 列會出現:"using index"。這裏也要強調一下不要使用「SELECT * 」,不然幾乎不可能使用到覆蓋索引。


【建議】:⑥在較長 VARCHAR 字段,例如 VARCHAR(100) 上創建索引時,應指定索引長度,不必對全字段創建索引,根據實際文本區分度決定索引長度便可

解讀:索引的長度與區分度是一對矛盾體,通常對字符串類型數據,若長度爲 20 的索引,區分度會高達 90% 以上,則能夠考慮建立長度例爲 20 的索引,而非全字段索引。


例如可使用 SELECT COUNT(DISTINCT LEFT(lesson_code, 20))/COUNT(*) FROM lesson;來肯定 lesson_code 字段字符長度爲 20 時文本區分度。


【建議】:⑦若是有 ORDER BY 的場景,請注意利用索引的有序性


ORDER BY 最後的字段是聯合索引的一部分,而且放在索引組合順序的最後,避免出現 file_sort 的狀況,影響查詢性能。


解讀:
  • 假設有查詢條件爲 WHERE a=? and b=? ORDER BY c;存在索引:a_b_c,則此時能夠利用索引排序。

  • 反例:在查詢條件中包含了範圍查詢,那麼索引有序性沒法利用,如:WHERE a>10 ORDER BY b;索引 a_b 沒法排序。


【建議】:⑧在 Where 中索引的列不能某個表達式的一部分,也不能是函數的參數


解讀: 便是某列上已經添加了索引,可是若此列成爲表達式的一部分、或者是函數的參數,MySQL 沒法將此列單獨解析出來,索引也不會生效。

【建議】:⑨咱們在 Where 條件中使用範圍查詢時,索引最多用於一個範圍條件,超過一個則後邊的不走索引


解讀: MySQL 可以使用多個範圍條件裏邊的最左邊的第一個範圍查詢,可是後邊的範圍查詢則沒法使用。

【建議】:⑩在多個表進行外鏈接時,表之間的關聯字段類型必須徹底一致


解讀: 當兩個表進行 Join 時,字段類型若沒有徹底一致,則加索引也不會生效,這裏的徹底一致包括但不限於字段類型、字段長度、字符集、Collection 等等。


參考資料:
  • 《High.Performance.MySQL.3rd.Edition》

  • 《阿里巴巴java開發手冊》


做者:浮雷

編輯:陶家龍

出處:https://juejin.im/post/6871969929365553165


更多好文敬請關注公衆號


本文分享自微信公衆號 - JAVA開發者課堂(leechence)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。

相關文章
相關標籤/搜索