Mysql命名規範

基礎規範

  • 表存儲引擎必須使用 InnoDB
  • 表字符集默認使用 utf8,必要時候使用 utf8mb4

    說明:
    1)通用,無亂碼風險,漢字3字節,英文1字節
    2)`utf8mb4` 是 `utf8` 的超集,有存儲 4 字節例如表情符號時,使用它
    
  • 禁止使用存儲過程,視圖,觸發器,Event

    說明:
    1)對數據庫性能影響較大,互聯網業務,能讓站點層和服務層乾的事情,不要交到數據庫層
    2)調試,排錯,遷移都比較困難,擴展性、移植性較差
    
  • 禁止在數據庫中存儲大文件,例如照片,可以將大文件存儲在對象存儲系統,數據庫中存儲路徑
  • 禁止在線上環境做數據庫壓力測試
  • 測試,開發,線上數據庫環境必須隔離

命名規範

  • 庫名與應用名稱儘量一致。
  • 表名、字段名必須使用小寫字母或數字,禁止出現數字開頭,禁止兩個下劃線中間只
    出現數字。

    說明:MySQL 在 Windows 下不區分大小寫,但在 Linux 下默認是區分大小寫。因此,數據庫名、表名、字段名,都不允許出現任何大寫字母,避免節外生枝。
    正例:`aliyun_admin`,`rdc_config`,`level3_name`
    反例:`AliyunAdmin`,`rdcConfig`,`level_3_name`
  • 庫名,表名,列名必須見名知義,長度不要超過 32 字符
    說明:tmpwushan 誰知道這些庫是幹嘛的
  • 禁用保留字,如 descrangematchdelayed 等,請參考 MySQL 官方保留字
  • 庫備份必須以 bak 爲前綴,以日期爲後綴
  • 從庫必須以 -s 爲後綴
  • 備庫必須以 -ss 爲後綴

表設計規範

  • 單實例表個數必須控制在 2000 個以內
  • 單表分表個數必須控制在 1024 個以內
  • 表必須有主鍵,推薦使用 UNSIGNED 整數爲主鍵
    潛在坑:刪除無主鍵的表,如果是 row 模式的主從架構,從庫會掛住
  • 禁止使用外鍵,如果要保證完整性,應由應用程式實現
    說明:外鍵使得表之間相互耦合,影響 update/delete 等 SQL 性能,有可能造成死鎖,高併發情況下容易成爲數據庫瓶頸
  • 建議將大字段,訪問頻度低的字段拆分到單獨的表中存儲,分離冷熱數據
  • 單錶行數超過 500 萬行或者單表容量超過 2GB,才推薦進行分庫分表。

    說明:如果預計三年後的數據量根本達不到這個級別,請不要在創建表時就分庫分表。
  • 表名不使用複數名詞。
    說明:表名應該僅僅表示表裏面的實體內容,不應該表示實體數量,對應類名也是單數形式,符合表達習慣。
  • 表的命名最好是加上“業務名稱_表的作用”。
    正例:alipay_task / force_project / trade_config
  • 表必備三字段:id, gmt_create, gmt_modified
    說明:其中 id 必爲主鍵,類型爲 unsigned bigint、單表時自增、步長爲 1。gmt_create, gmt_modified 的類型均爲 date_time 類型,前者現在時表示主動創建,後者過去分詞表示被動更新。

列設計規範

  • 小數類型爲 decimal,禁止使用 float  double
    說明:float  double 在存儲的時候,存在精度損失的問題,很可能在值的比較時,得到不正確的結果。
  • 如果存儲的數據範圍超過 decimal 的範圍,建議將數據拆成整數和小數分開存儲。
  • 根據業務區分使用 tinyint/int/bigint,分別會佔用 1/4/8 字節
  • 根據業務區分使用 char/varchar

    說明:
    1)字段長度固定,或者長度近似的業務場景,適合使用 `char`,能夠減少碎片,查詢性能高
    2)字段長度相差較大,或者更新較少的業務場景,適合使用 `varchar`,能夠減少空間
  • varchar 是可變長字符串,不預先分配存儲空間,長度不要超過 5000,如果存儲長度大於此值,定義字段類型爲 text,獨立出來一張表,用主鍵來對應,避免影響其它字段索引效率。
  • 根據業務區分使用 datetime/timestamp
    說明:前者佔用 5 個字節,後者佔用 4 個字節,存儲年使用 YEAR,存儲日期使用 DATE,存儲時間使用datetime
  • 必須把字段定義爲 NOT NULL 並設默認值

    說明:
    1)`NULL` 的列使用索引,索引統計,值都更加複雜,MySQL 更難優化
    2)`NULL` 需要更多的存儲空間
    3)`NULL` 只能採用 `IS NULL` 或者 `IS NOT NULL`,而在 `=`/`!=`/`in`/`not in` 時有大坑
  • 使用 INT UNSIGNED 存儲 IPv4,不要用 char(15)
  • 使用 varchar(20) 存儲手機號,不要使用整數

    說明:
    1)牽扯到國家代號,可能出現 `+/-/()` 等字符,例如 `+86`
    2)手機號不會用來做數學運算
    3)`varchar` 可以模糊查詢,例如 `like ‘138%’`
    
  • 使用 TINYINT 來代替 ENUM
    說明:ENUM 增加新值要進行 DDL 操作
  • 表達是與否概念的字段,必須使用 is_xxx 的方式命名,數據類型是 unsigned tinyint
    ( 1 表示是,0 表示否)。

    說明:任何字段如果爲非負數,必須是 `unsigned`。
    正例:表達邏輯刪除的字段名 `is_deleted`,1 表示刪除,0 表示未刪除。
    
  • 如果修改字段含義或對字段表示的狀態追加時,需要及時更新字段註釋。
  • 字段允許適當冗餘,以提高查詢性能,但必須考慮數據一致。冗餘字段應遵循:

    1)不是頻繁修改的字段。
    2)不是 `varchar` 超長字段,更不能是 `text` 字段。
    正例:商品類目名稱使用頻率高,字段長度短,名稱基本一成不變,可在相關聯的表中冗餘存儲類目名稱,避免關聯查詢。
    
  • 合適的字符存儲長度,不但節約數據庫表空間、節約索引存儲,更重要的是提升檢索速度。

    正例:如下表,其中無符號值可以避免誤存負數,且擴大了表示範圍。
    
對象 年齡區間 類型 字節 表示範圍
150 歲之內 unsigned tinyint 1 0 到 255
數百歲 unsigned smallint 2 無符號值:0 到 65535
恐龍化石 數千萬年 unsigned int 4 無符號值:0 到約 42.9 億
太陽 約 50 億年 unsigned bigint 8 無符號值:0 到約 10 的 19 次方

索引規範

  • 主鍵索引名爲 pk_[字段名];唯一索引名爲 uk_[字段名];普通索引名則爲 idx_[字段名];單張表索引數量建議控制在5個以內。
    說明:

    1)`pk_` 即 primary key;`uk_` 即 unique key;`idx_` 即 index 的簡稱。
    2)互聯網高併發業務,太多索引會影響寫性能
    3)生成執行計劃時,如果索引太多,會降低性能,並可能導致MySQL選擇不到最優索引
    4)異常複雜的查詢需求,可以選擇ES等更爲適合的方式存儲
    
  • 組合索引字段數不建議超過 5 個
    說明:如果 5 個字段還不能極大縮小 row 範圍,八成是設計有問題
  • 超過三個表禁止 join。需要 join 的字段,數據類型必須絕對一致;多表關聯查詢時,保證被關聯的字段需要有索引。
    說明:即使雙表 join 也要注意表索引、SQL 性能。踩過因爲 JOIN 字段類型不一致,而導致全表掃描的坑麼?
  • 理解組合索引最左前綴原則,避免重複建設索引,如果建立了 (a,b,c),相當於建立了 (a), (a,b), (a,b,c)
  •  varchar 字段上建立索引時,必須指定索引長度,沒必要對全字段建立索引,根據實際文本區分度決定索引長度即可。
    說明:索引的長度與區分度是一對矛盾體,一般對字符串類型數據,長度爲 20 的索引,區分度會高達 90% 以上,可以使用 count(distinct left(列名, 索引長度))/count(*) 的區分度來確定。
  • 頁面搜索嚴禁左模糊或者全模糊,如果需要請走搜索引擎來解決。
    說明:索引文件具有 B-Tree 的最左前綴匹配特性,如果左邊的值未確定,那麼無法使用此索引。
  • 如果有 order by 的場景,請注意利用索引的有序性。order by 最後的字段是組合索引的一部分,並且放在索引組合順序的最後,避免出現 file_sort 的情況,影響查詢性能。

    正例:`where a=? and b=? order by c;` 索引:`a_b_c`
    反例:索引中有範圍查找,那麼索引有序性無法利用,如:`WHERE a>10 ORDER BY b;` 索引 `a_b` 無法排序。
  • 利用覆蓋索引來進行查詢操作,避免回表。

    說明:如果一本書需要知道第 11 章是什麼標題,會翻開第 11 章對應的那一頁嗎?目錄瀏覽一下就好,這個目錄就是起到覆蓋索引的作用。
    正例:能夠建立索引的種類:主鍵索引、唯一索引、普通索引,而覆蓋索引是一種查詢的一種效果,用 `explain` 的結果,`extra` 列會出現:`using index`。
  • 利用延遲關聯或者子查詢優化超多分頁場景。

    說明:MySQL 並不是跳過 `offset` 行,而是取 `offset+N` 行,然後返回放棄前 `offset` 行,返回 N 行,那當 offset 特別大的時候,效率就非常的低下,要麼控制返回的總頁數,要麼對超過特定閾值的頁數進行 SQL 改寫。
    正例:先快速定位需要獲取的 `id` 段,然後再關聯:

    SELECT a.* FROM 表 1 a, (select id from 表 1 where 條件 LIMIT 100000,20 ) b where a.id=b.id

  • SQL 性能優化的目標:至少要達到 range 級別,要求是 ref 級別,如果可以是 consts 最好。
    說明:

    1)consts 單表中最多隻有一個匹配行(主鍵或者唯一索引),在優化階段即可讀取到數據。
    2)ref 指的是使用普通的索引(normal index)。
    3)range 對索引進行範圍檢索。
    反例:explain 表的結果,type=index,索引物理文件全掃描,速度非常慢,這個 index 級別比較 range 還低,與全表掃描是小巫見大巫。
  • 建組合索引的時候,區分度最高的在最左邊。

    正例:如果 `where a=? and b=?` ,a 列的幾乎接近於唯一值,那麼只需要單建 idx_a 索引即可。
    說明:存在非等號和等號混合判斷條件時,在建索引時,請把等號條件的列前置。如:`where a>? and b=?` 那麼即使 a 的區分度更高,也必須把 b 放在索引的最前列。
  • 防止因字段類型不同造成的隱式轉換,導致索引失效。
  • 創建索引時避免有如下極端誤解:

    1)寧濫勿缺。認爲一個查詢就需要建一個索引。
    2)寧缺勿濫。認爲索引會消耗空間、嚴重拖慢更新和新增速度。
    3)抵制惟一索引。認爲業務的惟一性一律需要在應用層通過“先查後插”方式解決。
    

SQL 語句

  • 禁止使用 select *,只獲取必要字段
    說明:

    1)`select` 會增加 cpu/io/內存/帶寬的消耗
    2)指定字段能有效利用索引覆蓋
    3)指定字段查詢,在表結構變更時,能保證對應用程序無影響
    
  • insert 必須指定字段,禁止使用 insert into T values()

    說明:指定字段插入,在表結構變更時,能保證對應用程序無影響
    
  • 不要使用 count(列名)  count(常量) 來替代 count(*)count(*) 是 SQL92 定義的標準統計行數的語法,跟數據庫無關,跟 NULL 和非 NULL 無關。

    說明:`count(*)` 會統計值爲 `NULL` 的行,而 `count(列名)` 不會統計此列爲 `NULL` 值的行。
    
  • count(distinct col) 計算該列除 NULL 之外的不重複行數,注意 count(distinct col1, col2) 如果其中一列全爲 NULL,那麼即使另一列有不同的值,也返回爲 0。
  • 當某一列的值全是 NULL 時,count(col) 的返回結果爲 0,但 sum(col) 的返回結果爲 NULL,因此使用 sum() 時需注意 NPE 問題。

    正例:可以使用如下方式來避免 `sum` 的 NPE 問題:`SELECT IF(ISNULL(SUM(g)),0,SUM(g)) FROM table;`
    註釋:*NPE* 全稱 NullPointerException 是指編程語言中的空指針異常。
    
  • 使用 ISNULL() 來判斷是否爲 NULL 值。

    說明:`NULL` 與任何值的直接比較都爲 `NULL`。
    1) `NULL<>NULL` 的返回結果是 `NULL`,而不是 `false`。
    2) `NULL=NULL` 的返回結果是 `NULL`,而不是 `true`。
    3) `NULL<>1` 的返回結果是 `NULL`,而不是 `true`。
    
  • 在代碼中寫分頁查詢邏輯時,若 count 爲 0 應直接返回,避免執行後面的分頁語句。
  • 不得使用外鍵與級聯,一切外鍵概念必須在應用層解決。

    說明:以學生和成績的關係爲例,學生表中的 `student_id` 是主鍵,那麼成績表中的 `student_id` 則爲外鍵。如果更新學生表中的 `student_id`,同時觸發成績表中的 `student_id` 更新,即爲級聯更新。外鍵與級聯更新適用於單機低併發,不適合分佈式、高併發集羣;級聯更新是強阻塞,存在數據庫更新風暴的風險;外鍵影響數據庫的插入速度。
    
  • 禁止使用存儲過程,存儲過程難以調試和擴展,更沒有移植性。
  • 數據訂正時,刪除和修改記錄時,要先 select,避免出現誤刪除,確認無誤才能執行更新語句。
  • in 操作能避免則避免,若實在避免不了,需要仔細評估 in 後邊的集合元素數量,控
    制在 1000 個之內。
  • 如果有全球化需要,所有的字符存儲與表示,均以 utf-8 編碼,注意字符統計函數的區別。

    說明:
    `SELECT LENGTH("輕鬆工作");` 返回爲 12;
    `SELECT CHARACTER_LENGTH("輕鬆工作");` 返回爲 4;
    如果需要存儲表情,那麼選擇 `utfmb4` 來進行存儲,注意它與 `utf-8` 編碼的區別。
    
  • TRUNCATE TABLE  DELETE 速度快,且使用的系統和事務日誌資源少,但 TRUNCATE 無事務且不觸發 trigger,有可能造成事故,故不建議在開發代碼中使用此語句。

    說明:`TRUNCATE TABLE` 在功能上與不帶 `WHERE` 子句的 `DELETE` 語句相同。
    

以上內容,整理自阿里巴巴 Java開發手冊和58到家MySQL軍規升級版。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章