數據庫設計規范

【MySQL】MySQL數據庫設計規范

1. 規范背景與?的

MySQL數據庫與 Oracle、 SQL Server 等數據庫相?,有其內核上的優勢與劣勢。我們在使?MySQL數據庫的時候需要遵循?定規范,揚長避短。本規范旨在幫助或指導RD、QA、OP等技術?員做出適合線上業務的數據庫設計。在數據庫變更和處理流程、數據庫表設計、SQL編寫等??予以規范,從?為公司業務系統穩定、健康地運?提供保障。

2. 設計規范

2.1 數據庫設計

以下所有規范會按照【?危】、【強制】、【建議】三個級別進?標注,遵守優先級從?到低。

對于不滿?【?危】和【強制】兩個級別的設計,DBA會強制打回要求修改。

2.1.1 庫名

  1. 【強制】庫的名稱必須控制在32個字符以內,相關模塊的表名與表名之間盡量提現join的關系,如user表和user_login表。
  2. 【強制】庫的名稱格式:業務系統名稱_?系統名,同?模塊使?的表名盡量使?統?前綴。
  3. 【強制】?般分庫名稱命名格式是庫通配名編號,編號從0開始遞增,?如wenda_001以時間進?分庫的名稱格式是“庫通配名時間”
  4. 【強制】創建數據庫時必須顯式指定字符集,并且字符集只能是utf8mb4。創建數據庫SQL舉例:create database db1 default character set utf8mb4;。

2.1.2 表結構

  1. 【強制】表和列的名稱必須控制在32個字符以內,表名只能使?字母、數字和下劃線,?律?寫。
  2. 【強制】表名要求模塊名強相關,如師資系統采?”sz”作為前綴,渠道系統采?”qd”作為前綴等。
  3. 【強制】創建表時必須顯式指定字符集為utf8或utf8mb4。
  4. 【強制】創建表時必須顯式指定表存儲引擎類型,如?特殊需求,?律為InnoDB。當需要使?除InnoDB/MyISAM/Memory以外的存儲引擎時,必須通過DBA審核才能在?產環境中使?。因為Innodb表?持事務、?鎖、宕機恢復、MVCC等關系型數據庫重要特性,為業界使?最多的MySQL存儲引擎。?這是其他?多數存儲引擎不具備的,因此?推InnoDB。
  5. 【強制】建表必須有comment
  6. 【建議】建表時關于主鍵:(1)強制要求主鍵為id,類型為int或bigint,且為auto_increment(2)標識表?每??主體的字段不要設為主鍵, 建議設為其他字段如user_id,order_id等, 并建?unique key索引(可參考cdb.teacher表設計)。因為如果設為主鍵且主鍵值為隨機插?, 則會導致innodb內部page分裂和?量隨機I/O,性能下降。
  7. 【建議】核?表(如?戶表,?錢相關的表)必須有?數據的創建時間字段create_time和最后更新時間字段update_time,便于查問題。
  8. 【建議】表中所有字段必須都是NOT NULL屬性,業務可以根據需要定義DEFAULT值。因為使?NULL值會存在每??都會占?額外存儲空間、數據遷移容易出錯、聚合函數計算結果偏差等問題。
  9. 【建議】建議對表?的blob、text等?字段,垂直拆分到其他表?,僅在需要讀這些對象的時候才去select。
  10. 【建議】反范式設計:把經常需要join查詢的字段,在其他表?冗余?份。如user_name屬性在user_account,user_login_log等表?冗余?份,減少join查詢。
  11. 【強制】中間表?于保留中間結果集,名稱必須以tmp_開頭。備份表?于備份或抓取源表快照,名稱必須以bak_開頭。中間表和備份表定期清理。
  12. 【強制】對于超過100W?的?表進?alter table,必須經過DBA審核,并在業務低峰期執?。因為alter table會產?表鎖,期間阻塞對于該表的所有寫?,對于業務可能會產?極?影響。

建表規約:

  1. 【強制】表達是與否概念的字段,必須使? is_xxx的?式命名,數據類型是 unsigned tinyint( 1表?是,0表?否)。
    1. 說明:任何字段如果為?負數,必須是 unsigned。
    2. 正例:表達邏輯刪除的字段名 is_deleted,1 表?刪除,0 表?未刪除。
  2. 【強制】表名、字段名必須使??寫字母或數字,禁?出現數字開頭,禁?兩個下劃線中間只出現數字。數據庫字段名的修改代價很?,因為?法進?預發布,所以字段名稱需要慎重考慮。
    1. 說明:MySQL 在 Windows 下不區分??寫,但在 Linux 下默認是區分??寫。因此,數據庫名、表名、字段名,都不允許出現任何?寫字母,避免節外?枝。
    2. 正例:aliyun_admin,rdc_config,level3_name
    3. 反例:AliyunAdmin,rdcConfig,level_3_name
  3. 【強制】表名不使?復數名詞。
    1. 說明:表名應該僅僅表?表??的實體內容,不應該表?實體數量,對應于 DO類名也是單數
    2. 形式,符合表達習慣。
  4. 【強制】禁?保留字,如 desc、range、match、delayed等,請參考 MySQL官?保留字。
  5. 【強制】主鍵索引名為 pk_字段名;唯?索引名為 uk_字段名;普通索引名則為 idx_字段名。
    1. 說明:pk_ 即 primary key;uk_ 即 unique key;idx_ 即 index的簡稱。
  6. 【強制】?數類型為 decimal,禁?使? float和 double。
    1. 說明:float和 double在存儲的時候,存在精度損失的問題,很可能在值的?較時,得到不正確的結果。如果存儲的數據范圍超過decimal的范圍,建議將數據拆成整數和?數分開存儲。
  7. 【強制】如果存儲的字符串長度?乎相等,使? char定長字符串類型。
  8. 【強制】varchar是可變長字符串,不預先分配存儲空間,長度不要超過 5000,如果存儲長度?于此值,定義字段類型為text,獨?出來?張表,?主鍵來對應,避免影響其它字段索引效率。
  9. 【強制】表必備三字段:id, gmt_create, gmt_modified。
    1. 說明:其中 id必為主鍵,類型為 unsigned bigint、單表時?增、步長為 1。gmt_create,gmt_modified的類型均為 date_time類型,前者現在時表?主動創建,后者過去分詞表?被動更新。
  10. 【推薦】表的命名最好是加上“業務名稱_表的作?”。
  11. 正例:alipay_task / force_project / trade_config
  12. 【推薦】庫名與應?名稱盡量?致。
  13. 【推薦】如果修改字段含義或對字段表?的狀態追加時,需要及時更新字段注釋。
  14. 【推薦】字段允許適當冗余,以提?查詢性能,但必須考慮數據?致。冗余字段應遵循:
  15. 1)不是頻繁修改的字段。
  16. 2)不是 varchar超長字段,更不能是 text字段。
  17. 正例:商品類?名稱使?頻率?,字段長度短,名稱基本?成不變,可在相關聯的表中冗余存儲類?名稱,避免關聯查詢。
  18. 【推薦】單表?數超過 500萬?或者單表容量超過 2GB,才推薦進?分庫分表。
  19. 說明:如果預計三年后的數據量根本達不到這個級別,請不要在創建表時就分庫分表。
  20. 【參考】合適的字符存儲長度,不但節約數據庫表空間、節約索引存儲,更重要的是提升檢索速度。

其它:

  1. INT 類型不使? unsigned ?符號屬性,容易引?額外的計算問題。
  2. ?增? 8 字節 BIG INT,不要使? 4 字節 INT,且?增在 MySQL 8.0 版本前有回溯問題,請考慮是否業務有影響。
  3. 字符集使? UTF8MB4 字符編碼,不推薦 GBK、UTF-8 等其他字符集。
  4. ?期類型? DATETIME 類型,需要精確到毫秒? DATETIME(6),不要使? INT、TIMESTAMP。
  5. 類型 JSON 可?于存儲?結構化數據,典型場景為?戶標簽,不要將 JSON ?于頻繁更新的字段場景。
  6. 每張表?定要有?個主鍵,這樣?少滿??范式的要求,核?業務表?全局唯?字段(雪花算法、有序UUID)做主鍵,不要使??增做主鍵。
  7. 對于?志類的流?表、報警表、?志表,可以使?壓縮設計,提升存儲效率。MySQL 5.7 版本開始推薦使?透明頁壓縮,不要使?傳統的 KEY_BLOCK_SIZE 的頁壓縮。
  8. 類別設計,? ENUM+CHECK 約束,不要使? INT 類型的設計。
  9. 敏感字段需加密,如賬戶密碼、信?卡號等存儲使?:動態鹽 + ?固定加密算法(MD5/AES256等) + 多輪加密,不要簡單使? MD5算法加密,容易被暴?破解。
  10. MySQL 可以通過 KV 的?式訪問表中的數據,若業務只是簡單的 SET、GET 請求,可考慮將其轉化為 Memcached 的 KV 訪問?式,減少 SQL 解析的開銷,性能可以有?少 50% 的提升。

2.1.3 列數據類型優化

  1. 【建議】表中的?增列(auto_increment屬性),推薦使?bigint類型。因為?符號int存儲范圍為-2147483648~2147483647(?約21億左右),溢出后會導致報錯。
  2. 【建議】業務中選擇性很少的狀態status、類型type等字段推薦使?tinytint或者smallint類型節省存儲空間。
  3. 【建議】業務中IP地址字段推薦使用int類型,不推薦?char(15)。因為int只占4字節,可以?如下函數相互轉換,而char(15)占??少15字節。?旦表數據?數到了1億,那么要多?1.1G存儲空間。 SQL:select inet_aton('192.168.2.12'); select in et_ntoa(3232236044); PHP: ip2long(‘192.168.2.12’); long2ip(3530427185);
  4. 【建議】不推薦使?enum,set。因為它們浪費空間,且枚舉值寫死了,變更不?便。推薦使?tinyint或smallint。
  5. 【建議】不推薦使?blob,text等類型。它們都?較浪費硬盤和內存空間。在加載表數據時,會讀取?字段到內存?從?浪費內存空間,影響系統性能。建議和PM、RD溝通,是否真的需要這么?字段。Innodb中當??記錄超過8098字節時,會將該記錄中選取最長的?個字段將其768字節放在原始page?,該字段余下內容放在overflow-page?。不幸的是在compact?格式下,原始page和overflow-page都會加載。
  6. 【建議】存儲?錢的字段,建議?int,程序端乘以100和除以100進?存取。因為int占?4字節,?double占?8字節,空間浪費。
  7. 【建議】?本數據盡量?varchar存儲。因為varchar是變長存儲,?char更省空間。MySQL server層規定??所有?本最多存65535字節,因此在utf8字符集下最多存21844個字符,超過會?動轉換為mediumtext字段。?text在utf8字符集下最多存21844個字符,mediumtext最多存224/3個字符,longtext最多存232個字符。?般建議?varchar類型,字符數不要超過2700。
  8. 【建議】時間類型盡量選取timestamp。因為datetime占?8字節,timestamp僅占?4字節,但是范圍為1970-01-01 00:00:01到2038-01-01 00:00:00。更為?階的?法,選?int來存儲時間,使?SQL函數unix_timestamp()和from_unixtime()來進?轉換。

2.1.4 索引設計

  1. 【強制】InnoDB表必須主鍵為id int/bigint auto_increment,且主鍵值禁?被更新。
  2. 【建議】主鍵的名稱以“pk_”開頭,唯?鍵以“uk_”或“uq_”開頭,普通索引以“idx_”開頭,?律使??寫格式,以表名/字段的名稱或縮寫作為后綴。
  3. 【強制】InnoDB和MyISAM存儲引擎表,索引類型必須為BTREE;MEMORY表可以根據需要選擇HASH或者BTREE類型索引。
  4. 【強制】單個索引中每個索引記錄的長度不能超過64KB。
  5. 【建議】單個表上的索引個數不能超過7個。
  6. 【建議】在建?索引時,多考慮建?聯合索引,并把區分度最?的字段放在最前?。如列userid的區分度可由select count(distinct userid)計算出來。
  7. 【建議】在多表join的SQL?,保證被驅動表的連接列上有索引,這樣join執?效率最?。
  8. 【建議】建表或加索引時,保證表?互相不存在冗余索引。對于MySQL來說,如果表?已經存在key(a,b),則key(a)為冗余索引,需要刪除。

索引規約:

  1. 【強制】業務上具有唯?特性的字段,即使是多個字段的組合,也必須建成唯?索引。
    1. 說明:不要以為唯?索引影響了 insert速度,這個速度損耗可以忽略,但提?查找速度是明顯的;另外,即使在應?層做了? 常完善的校驗控制,只要沒有唯?索引,根據墨菲定律,必然有臟數據產?。
  2. 【強制】超過三個表禁? join。需 要 join的字段,數據類型必須絕對?致;多表關聯查詢時,保證被關聯的字段需要有索引。
    1. 說明:即使雙表 join也要注意表索引、SQL性能。
  3. 【強制】在 varchar字段上建?索引時,必須指定索引長度,沒必要對全字段建?索引,根據實際?本區分度決定索引長度可。
    1. 說明:索引的長度與區分度是?對?盾體,?般對字符串類型數據,長度為 20的索引,區分度會?達 90%以上,可以使? count(distinct left(列名, 索引長度))/count(*)的區分度來確定。
  4. 【強制】頁?搜索嚴禁左模糊或者全模糊,如果需要請?搜索引擎來解決。
    1. 說明:索引?件具有 B-Tree的最左前綴匹配特性,如果左邊的值未確定,那么?法使?此索引。
  5. 【推薦】如果有 order by的場景,請注意利?索引的有序性。order by 最后的字段是組合索引的?部分,并且放在索引組合順序的最后,避免出現 file_sort的情況,影響查詢性能。
    1. 正例:where a=? and b=? order by c; 索引:a_b_c
    2. 反例:索引中有范圍查找,那么索引有序性?法利?,如:WHERE a>10 ORDER BY b; 索引a_b?法排序。
  6. 【推薦】利?覆蓋索引來進?查詢操作,避免回表。
    1. 說明:如果?本書需要知道第 11章是什么標題,會翻開第 11章對應的那?頁嗎??錄瀏覽?下就好,這個?錄就是起到覆蓋索引的作?。
    2. 正例:能夠建?索引的種類:主鍵索引、唯?索引、普通索引,?覆蓋索引是?種查詢的?種效果,? explain的結果,extra列會出現:using index。
  7. 【推薦】利?延遲關聯或者?查詢優化超多分頁場景。
    1. 說明:MySQL并不是跳過 offset?,?是取 offset+N?,然后返回放棄前 offset?,返回N?,那當 offset特別?的時候,效率就?常的低下,要么控制返回的總頁數,要么對超過特定閾值的頁數進? SQL改寫。
    2. 正例:先快速定位需要獲取的 id段,然后再關聯:
      1. SELECT a.* FROM 表 1 a, (select id from 表 1 where 條件 LIMIT 100000,20 ) b where a.id=b.id
  8. 【推薦】SQL性能優化的?標:?少要達到 range 級別,要求是 ref級別,如果可以是 consts最好。
    1. 說明:
      1. consts 單表中最多只有?個匹配?(主鍵或者唯?索引),在優化階段即可讀取到數據。
      2. ref 指的是使?普通的索引(normal index)。
      3. range 對索引進?范圍檢索。
    2. 反例:explain表的結果,type=index,索引物理?件全掃描,速度?常慢,這個 index級別?較 range還低,與全表掃描是?巫見?巫。
  9. 【推薦】建組合索引的時候,區分度最?的在最左邊。
    1. 正例:如果 where a=? and b=? ,a列的?乎接近于唯?值,那么只需要單建 idx_a索引即可。
    2. 說明:存在?等號和等號混合判斷條件時,在建索引時,請把等號條件的列前置。如:where a>?and b=? 那么即使 a的區分度更?,也必須把 b放在索引的最前列。
  10. 【推薦】防?因字段類型不同造成的隱式轉換,導致索引失效。
  11. 【參考】創建索引時避免有如下極端誤解:
  12. 寧濫勿缺。認為?個查詢就需要建?個索引。
  13. 寧缺勿濫。認為索引會消耗空間、嚴重拖慢更新和新增速度。
  14. 抵制惟?索引。認為業務的惟?性?律需要在應?層通過“先查后插”?式解決。

其它:

  1. 不要陷入設置單表行數、列數限制的固有印象,其他關系型數據庫沒有?數、列數限制,MySQL 也沒有,?表的缺點不是性能,?是后續的 DDL 管理問題,隨著 MySQL 8.0 快速加列功能的上線,?表 DDL 問題基本已解決。
  2. MySQL 是索引組織表,表中的數據以 B+ 樹索引結構,根據主鍵邏輯排序,由于 B+ 樹索引的特點是樹的?度為 3~4 層,所以從數?億的記錄中,通過主鍵查詢?條記錄只需要 3、4 次 I/O,當前到 SSD 存儲設備設置每秒?少能完成 10000 次的 I/O 查詢,不要擔?通過索引查詢?條或?條記錄的性能,每秒百萬次查詢并不難。
  3. MySQL 是索引組織表,?級索引只存儲(鍵值、主鍵值),因此需要再通過?次主鍵索引查詢得到記錄,這種?式成為回表。在核?業務中,使?索引覆蓋技術,提升索引查詢性能,對于回表記錄數?較?的場景,甚?可以有 10 倍的性能提升;
  4. 對類似 WHERE a = ? ORDER BY b 這樣的查詢,?定要創建(a、b)組合索引,這樣可以避免?次額外排序,提升查詢性能。
  5. MySQL 優化器是 CBO(Cost-based Optimizer),所有查詢基于成本而不是規則,若發現 SQL 執行計劃發?變化,不要懷疑 MySQL出錯,請先分析數據特點、索引創建是否合理,是否可以通過直?圖校準數據。
  6. MySQL JOIN ?持 NLJ(Nested Loop Join)和 NHJ(Nested Hash Join)兩種?式。對于 OLTP 業務,放??膽使? JOIN,但?定要做好索引的設計和索引覆蓋的考慮(不考慮分布式數據庫場景);對于 OLAP 業務,MySQL 8.0 版本開始,?持 Hash Join,對于?數據量的關聯,性能提升?常多,可以在不超過 10T 的數倉場景中考慮使?,超過 10T數據量,請?定使??數據產品,如 Hive、Spark、麒麟等產品。
  7. MySQL 5.7 版本開始?查詢優化已經做得不錯,但是編寫的?查詢不能是關聯?查詢,上線前?定需要確認,若發現關聯?查詢,請改寫?查詢為 JOIN 或其他?式。
  8. 不要因為數據量?,使?分區表,MySQL 是索引組織表,數據量再?,定位記錄也只需要3、4 次 I/O。可以考慮分區表唯?的應?場景是:需要定期清理歷史流?類數據,但如果業務可以按?、按天做分表,那么當前 MySQL 8.0 版本,分區表也不推薦使用。
  9. 業務上線或新版本發布前,DBA ?定要進行所有 SQL Review,確保 SQL ?索引,否則不予上線,或由業務以郵件等正式?式,通知DBA 該 SQL 不會引起線上事故,業務?承擔后續責任。
  10. DBA 每天要對數據庫進行巡檢,及早發現慢查詢或潛在數據庫風險,將任何潛在問題盡早拋出,否則后續自己承擔相關責任。

2.1.5 分庫分表、分區表

  1. 【強制】分區表的分區字段(partition-key)必須有索引,或者是組合索引的?列。
  2. 【強制】單個分區表中的分區(包括?分區)個數不能超過1024。
  3. 【強制】上線前RD或者DBA必須指定分區表的創建、清理策略。
  4. 【強制】訪問分區表的SQL必須包含分區鍵。
  5. 【建議】單個分區?件不超過2G,總??不超過50G。建議總分區數不超過20個。
  6. 【強制】對于分區表執?alter table操作,必須在業務低峰期執?。
  7. 【強制】采?分庫策略的,庫的數量不能超過1024
  8. 【強制】采?分表策略的,表的數量不能超過4096
  9. 【建議】單個分表不超過500W?,ibd?件??不超過2G,這樣才能讓數據分布式變得性能更佳。
  10. 【建議】?平分表盡量?取模?式,?志、報表類數據建議采??期進?分表。

2.1.6 字符集

  1. 【強制】數據庫本?庫、表、列所有字符集必須保持?致,為utf8或utf8mb4。
  2. 【強制】前端程序字符集或者環境變量中的字符集,與數據庫、表的字符集必須?致,統?為utf8。

2.1.7 程序層DAO設計建議

  1. 【建議】新的代碼不要?model,推薦使??動拼SQL+綁定變量傳?參數的?式。因為model雖然可以使??向對象的?式操作db, 但是其使?不當很容易造成?成的SQL非常復雜,且model層自己做的強制類型轉換性能較差,最終導致數據庫性能下降。
  2. 【建議】前端程序連接MySQL或者redis,必須要有連接超時和失敗重連機制,且失敗重試必須有間隔時間。
  3. 【建議】前端程序報錯?盡量能夠提?MySQL或redis原?態的報錯信息,便于排查錯誤。
  4. 【建議】對于有連接池的前端程序,必須根據業務需要配置初始、最小、最大連接數,超時時間以及連接回收機制,否則會耗盡數據庫連接資源,造成線上事故。
  5. 【建議】對于log或history類型的表,隨時間增長容易越來越?,因此上線前RD或者DBA必須建?表數據清理或歸檔?案。
  6. 【建議】在應?程序設計階段,RD必須考慮并規避數據庫中主從延遲對于業務的影響。盡量避免從庫短時延遲(20秒以內)對業務造成影響,建議強制?致性的讀開啟事務走主庫,或更新后過?段時間再去讀從庫。
  7. 【建議】多個并發業務邏輯訪問同?塊數據(innodb表)時,會在數據庫端產生行鎖甚?表鎖導致并發下降,因此建議更新類SQL盡量基于主鍵去更新。
  8. 【建議】業務邏輯之間加鎖順序盡量保持?致,否則會導致死鎖。
  9. 【建議】對于單表讀寫比大于10:1的數據?或單個列,可以將熱點數據放在緩存里(如mecache或redis), 加快訪問速度,降低MySQL壓力。

2.1.8 ?個規范的建表語句?例

?個較為規范的建表語句為:

CREATE TABLE user (`id` bigint(11) NOT NULL AUTO_INCREMENT,`user_id` bigint(11) NOT NULL COMMENT ‘?戶id’`username` varchar(45) NOT NULL COMMENT '真實姓名',`email` varchar(30) NOT NULL COMMENT ‘?戶郵箱’,`nickname` varchar(45) NOT NULL COMMENT '昵稱',`avatar` int(11) NOT NULL COMMENT '頭像',`birthday` date NOT NULL COMMENT '??',`sex` tinyint(4) DEFAULT '0' COMMENT '性別',`short_introduce` varchar(150) DEFAULT NULL COMMENT '?句話介紹??,最多50個漢字',`user_resume` varchar(300) NOT NULL COMMENT '?戶提交的簡歷存放地址',`user_register_ip` int NOT NULL COMMENT ‘?戶注冊時的源ip’,`create_time` timestamp NOT NULL COMMENT ‘?戶記錄創建的時間’,`update_time` timestamp NOT NULL COMMENT ‘?戶資料修改的時間’,`user_review_status` tinyint NOT NULL COMMENT ‘?戶資料審核狀態,1為通過,2為審核中,3為未通過,4為還未提交審核’,PRIMARY KEY (`id`),UNIQUE KEY `idx_user_id` (`user_id`),KEY `idx_username`(`username`),KEY `idx_create_time`(`create_time`,`user_review_status`)) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='?站?戶基本信息';

2.2 SQL編寫

2.2.1 DML語句

  1. 【強制】SELECT語句必須指定具體字段名稱,禁?寫成*。因為select *會將不該讀的數據也從MySQL?讀出來,造成?卡壓?。且表字段?旦更新,但model層沒有來得及更新的話,系統會報錯。
  2. 【強制】insert語句指定具體字段名稱,不要寫成insert into t1 values(…),道理同上。
  3. 【建議】insert into…values(XX),(XX),(XX)…。這?XX的值不要超過5000個。值過多雖然上線很很快,但會引起主從同步延遲。
  4. 【建議】SELECT語句不要使?UNION,推薦使?UNION ALL,并且UNION?句個數限制在5個以內。因為union all不需要去重,節省數據庫資源,提?性能。
  5. 【建議】in值列表限制在500以內。例如select… where userid in(….500個以內…),這么做是為了減少底層掃描,減輕數據庫壓?從?加速查詢。
  6. 【建議】事務?批量更新數據需要控制數量,進?必要的sleep,做到少量多次。
  7. 【強制】事務涉及的表必須全部是innodb表。否則?旦失敗不會全部回滾,且易造成主從庫同步終端。
  8. 【強制】寫?和事務發往主庫,只讀SQL發往從庫。
  9. 【強制】除靜態表或?表(100?以內),DML語句必須有where條件,且使?索引查找。
  10. 【強制】?產環境禁?使?hint,如sql_no_cache,force index,ignore key,straight join等。因為hint是?來強制SQL按照某個執?計劃來執行,但隨著數據量變化我們?法保證??當初的預判是正確的,因此我們要相信MySQL優化器!
  11. 【強制】where條件?等號左右字段類型必須?致,否則?法利?索引。
  12. 【建議】SELECT|UPDATE|DELETE|REPLACE要有WHERE?句,且WHERE?句的條件必需使?索引查找。
  13. 【強制】?產數據庫中強烈不推薦?表上發?全表掃描,但對于100?以下的靜態表可以全表掃描。查詢數據量不要超過表?數的25%,否則不會利?索引。
  14. 【強制】WHERE ?句中禁?只使?全模糊的LIKE條件進?查找,必須有其他等值或范圍查詢條件,否則?法利?索引。
  15. 【建議】索引列不要使?函數或表達式,否則?法利?索引。如where length(name)='Admin'或where user_id+2=10023。
  16. 【建議】減少使?or語句,可將or語句優化為union,然后在各個where條件上建?索引。如where a=1 or b=2優化為where a=1… union …where b=2, key(a),key(b)。
  17. 【建議】分頁查詢,當limit起點較?時,可先?過濾條件進?過濾。如select a,b,c from t1 limit 10000,20;優化為: select a,b,c from t1 where id>10000 limit 20;。

其它:

  1. 【強制】不要使? count(列名)或 count(常量)來替代 count(),count()是 SQL92定義的
    1. 標準統計?數的語法,跟數據庫?關,跟 NULL和? NULL?關。
    2. 說明:count(*)會統計值為 NULL的?,? count(列名)不會統計此列為 NULL值的?。
  2. 【強制】count(distinct col) 計算該列除 NULL之外的不重復?數,注意 count(distinctcol1, col2) 如果其中?列全為 NULL,那么即使另?列有不同的值,也返回為 0。
  3. 【強制】當某?列的值全是 NULL時,count(col)的返回結果為 0,但 sum(col)的返回結果為NULL,因此使? sum()時需注意 NPE問題。
    1. 正例:可以使?如下?式來避免 sum的 NPE問題:SELECT IF(ISNULL(SUM(g)),0,SUM(g))FROM table;
  4. 【強制】使? ISNULL()來判斷是否為 NULL值。
    1. 說明:NULL與任何值的直接?較都為 NULL。
      1. NULL<>NULL的返回結果是 NULL,?不是 false。
      2. NULL=NULL的返回結果是 NULL,?不是 true。
      3. NULL<>1的返回結果是 NULL,?不是 true。
  5. 【強制】 在代碼中寫分頁查詢邏輯時,若 count為 0應直接返回,避免執?后?的分頁語句。
  6. 【強制】不得使?外鍵與級聯,?切外鍵概念必須在應?層解決。
    1. 說明:以學?和成績的關系為例,學?表中的 student_id是主鍵,那么成績表中的 student_id為外鍵。如果更新學?表中的
    2. student_id,同時觸發成績表中的 student_id更新,即為級聯更新。外鍵與級聯更新適?于單機低并發,不適合分布式、?并發集群;級聯更新是強阻塞,存在數據庫更新風暴的風險;外鍵影響數據庫的插?速度。
  7. 【強制】禁?使?存儲過程,存儲過程難以調試和擴展,更沒有移植性。
  8. 【強制】數據訂正時,刪除和修改記錄時,要先 select,避免出現誤刪除,確認?誤才能執?更新語句。
  9. 【推薦】in操作能避免則避免,若實在避免不了,需要仔細評估 in后邊的集合元素數量,控制在 1000個之內。
  10. 【參考】如果有全球化需要,所有的字符存儲與表?,均以 utf8mb4編碼,注意字符統計函數的區別。
  11. 說明:
    1. SELECT LENGTH("輕松?作"); 返回為 12
    2. SELECT CHARACTER_LENGTH("輕松?作"); 返回為 4
  12. 如果需要存儲表情,那么選擇 utf8mb4來進?存儲,注意它與 utf-8編碼的區別。
  13. 【參考】TRUNCATE TABLE ? DELETE 速度快,且使?的系統和事務?志資源少,但 TRUNCATE?事務且不觸發trigger有可能造成事故,故不建議在開發代碼中使?此語句。
  14. 說明:TRUNCATE TABLE 在功能上與不帶 WHERE ?句的 DELETE 語句相同。

2.2.2 多表連接

  1. 【強制】禁?跨db的join語句。因為這樣可以減少模塊間耦合,為數據庫拆分奠定堅實基礎。
  2. 【強制】禁?在業務的更新類SQL語句中使?join,?如update t1 join t2…。
  3. 【建議】不建議使??查詢,建議將?查詢SQL拆開結合程序多次查詢,或使?join來代替?查詢。
  4. 【建議】線上環境,多表join不要超過3個表。
  5. 【建議】多表連接查詢推薦使?別名,且SELECT列表中要?別名引?字段,數據庫.表格式,如select a from db1.table1 alias1 where …。
  6. 【建議】在多表join中,盡量選取結果集較?的表作為驅動表,來join其他表。

2.2.3 事務

  1. 【建議】事務中INSERT|UPDATE|DELETE|REPLACE語句操作的?數控制在2000以內,以及WHERE?句中IN列表的傳參個數控制在500以內。
  2. 【建議】批量操作數據時,需要控制事務處理間隔時間,進?必要的sleep,?般建議值5-10秒。
  3. 【建議】對于有auto_increment屬性字段的表的插?操作,并發需要控制在200以內。
  4. 【強制】程序設計必須考慮“數據庫事務隔離級別”帶來的影響,包括臟讀、不可重復讀和幻讀。線上建議事務隔離級別為repeatable-read。
  5. 【建議】事務?包含SQL不超過5個(?付業務除外)。因為過長的事務會導致鎖數據較久,MySQL內部緩存、連接消耗過多等雪崩問題。
  6. 【建議】事務?更新語句盡量基于主鍵或unique key,如update … where id=XX; 否則會產?間隙鎖,內部擴?鎖定范圍,導致系統性能下降,產?死鎖。
  7. 【建議】盡量把?些典型外部調?移出事務,如調?webservice,訪問?件存儲等,從?避免事務過長。
  8. 【建議】對于MySQL主從延遲嚴格敏感的select語句,請開啟事務強制訪問主庫。

2.2.4 排序和分組

  1. 【建議】減少使?order by,和業務溝通能不排序就不排序,或將排序放到程序端去做。order by、group by、distinct這些語句較為耗費CPU,數據庫的CPU資源是極其寶貴的。
  2. 【建議】order by、group by、distinct這些SQL盡量利?索引直接檢索出排序好的數據。如where a=1 order by b可以利?key(a,b)。
  3. 【建議】包含了order by、group by、distinct這些查詢的語句,where條件過濾出來的結果集請保持在1000?以內,否則SQL會很慢。2.2.5 線上禁?使?的SQL語句
  4. 【?危】禁?update|delete t1 … where a=XX limit XX;這種帶limit的更新語句。因為會導致主從不?致,導致數據錯亂。建議加上order by PK。
  5. 【?危】禁?使?關聯?查詢,如update t1 set … where name in(select name from user where…);效率極其低下。
  6. 【強制】禁?procedure、function、trigger、views、event、外鍵約束。因為他們消耗數據庫資源,降低數據庫實例可擴展性。推薦都在程序端實現。
  7. 【強制】禁?insert into …on duplicate key update…在?并發環境下,會造成主從不?致。
  8. 【強制】禁?聯表更新語句,如update t1,t2 where t1.id=t2.id…。

2.2.5 ORM映射

  1. 【強制】在表查詢中,?律不要使? * 作為查詢的字段列表,需要哪些字段必須明確寫明。
    1. 說明:1)增加查詢分析器解析成本。2)增減字段容易與 resultMap配置不?致。
  2. 【強制】POJO類的布爾屬性不能加 is,?數據庫字段必須加 is_,要求在 resultMap中進?字段與屬性之間的映射。
    1. 說明:參見定義 POJO類以及數據庫字段定義規定,在<resultMap>中增加映射,是必須的。在 MyBatis Generator?成的代碼中,需要進?對應的修改。
  3. 【強制】不要? resultClass當返回參數,即使所有類屬性名與數據庫字段??對應,也需要定義;反過來,每?個表也必然有?個與之對應。
    1. 說明:配置映射關系,使字段與 DO類解耦,?便維護。
  4. 【強制】sql.xml配置參數使?:#{},#param# 不要使?${} 此種?式容易出現 SQL注?。
  5. 【強制】MYBATIS?帶的 queryForList(String statementName,int start,int size)不推薦使?。
    1. 說明:其實現?式是在數據庫取到statementName對應的SQL語句的所有記錄,再通過subList取 start,size的?集合。
    2. 正例:Map<String, Object> map = new HashMap<String, Object>();
      1. map.put("start", start);
      2. map.put("size", size);
  6. 【強制】不允許直接拿 HashMap與 Hashtable作為查詢結果集的輸出。
    1. 說明:resultClass=”Hashtable”,會置?字段名和屬性值,但是值的類型不可控。
  7. 【強制】更新數據表記錄時,必須同時更新記錄對應的 gmt_modified字段值為當前時間。
  8. 【推薦】不要寫?個??全的數據更新接?傳?為 POJO類,不管是不是??的?標更新字段,都進? update table set c1=value1,c2=value2,c3=value3; 這是不對的。執?SQL時,不要更新?改動的字段,?是易出錯;?是效率低;三是增加 binlog存儲。
  9. 【參考】@Transactional事務不要濫?。事務會影響數據庫的 QPS,另外使?事務的地?需要考慮各??的回滾?案,包括緩存回滾、搜索引擎回滾、消息補償、統計修正等。
  10. 【參考】<isEqual>中的 compareValue是與屬性值對?的常量,?般是數字,表?相等時帶 上此條件;<isNotEmpty>表?不為空且不為 null時執?;<isNotNull>表?不為 null值時執?。

2.3 ?可?架構

  1. MySQL ?可?的基?是利??進制?志的復制技術,核?業務?定要使??損半同步?式,但凡不適??損半同步的?可?架構,請業務?業務以郵件等正式?式回復,數據丟失等后續問題??承擔相關責任。
  2. MySQL 5.7 版本開始,?定要使?基于 WRITESET 的從機回放,避免主從延遲。
  3. 當前 MySQL 發?主從延遲的可能性主要是存在?事務,?如定期計算收益等操作,這類?事務,?定要通知業務?將?事務拆成?事務,否則不予上線;若要上線,請業務?以郵件等?式回復,??承擔后續主從延遲帶來的后續?系列問題。
  4. MySQL 8.0 版本開始推薦?融業務使? MGR(MySQL Group Replication),通過 Paxos 協議保證數據?致性,并??完成選主的邏輯,也可以使?多主模式,數據不沖突的情況下,可以?幅提升寫?性能。
  5. 對于核?業務,必須遵循互不信任原則,數據?致性不單單依賴 MySQL 復制本?,DBA 這?需要通過邏輯的?式,對主從數據進?核對,業務這?也需要?套業務層的邏輯進?“對賬”。
  6. 核?業務,務必使??地三中?,兩地三中?的跨機房復制架構,這樣發?機房級故障,可以切換到另?個機房,保證業務可?性。
  7. 同城容災架構?定要評估切換到另?個機房后業務訪問的性能,多次跨機房訪問 DB,雖然每次只多了 2~3ms,但也存在業務雪崩問題,推薦 DB 切換機房,上層業務跟著?起聯動切換。
  8. 對于有跨城容災需求的業務,可以考慮使?三地五中?架構,但是由于 30ms 延遲,業務需要進?評估,對于核?業務,務必使?業務層的跨城機制,將數據層的多次?絡耗時合并為?次,這樣能??提升業務的性能。
  9. 業界的 MHA、Ochestrator 等?可?套件都是基于 ssh 訪問 MySQL,穩定性、安全性不?,不推薦??使?;??開發?個數據庫管理平臺,通過 agent 的模式管理?可?和 MySQL 數據庫的?常操作更為安全、有效。
  10. ?定做好數據備份架構的設計,全備 + 增量備份 + 延遲備機(可選),做到可以基于任何?點恢復和回滾,同時,遵循互不信任原則,備份?件?定要進?檢查,確保需要時?定能夠進?恢復。

2.4 分布式架構

  1. 分布式數據庫的本質就是根據某?個列的規則,將數據?平打散,存在不同的實例中。數據拆分的列就成為分區鍵,分區鍵?定是業務?部分訪問(超過 80%)的表都會使?的列。若選不出合適的分區鍵,那就?定不要進?分布式數據庫架構的設計;互聯?業務絕?部分分區鍵的選擇是?戶維度。
  2. 分區算法絕?部分場景使? Hash算法,這樣數據的存儲和訪問可以平均到下?多個實例,真正的做到可擴展性,Range 算法通常無法解決熱點問題,會是災難,但 Range 算法可以但實例中使?,作為?級拆分數據的規則。
  3. 分布式數據庫分?時,?開始就設計為不少于 1000 個分?的規則,不?擔?分?過多的問題,管理 1 個分?和 1000 個分?的成本是?樣的,但為后續的擴展做好了充?的準備。
  4. 分布式數據庫擴縮容就是通過部分過濾的復制技術,按庫或按表進?數據同步,分庫分表設計推薦庫名、表都不同,做到全局唯?,?便后續拆分。
  5. 分布式數據庫索引設計中,?分區鍵的唯?索引?定帶?分區鍵信息,這樣業務查詢時可以直接定位到數據所在分?,提升查詢效率。
  6. 分布式數據庫索引設計中,數據庫層的唯?約束只在單個實例中保證,若要保證全局唯?,?定要使?全局唯?的索引設計。
  7. 直接使? JOIN 請確認?定可以單元化在?個分?中完成,如果涉及跨分?的 JOIN,請通知業務修改成多條 SQL 的訪問?式,只訪問指定分??不是所有分?。
  8. 分布式數據庫可以進?業務層的分庫分表訪問,和通過數據庫中間件的訪問,對于業務耗時敏感的業務,推薦業務層直接根據路由規則訪問數據,否則使?數據庫中間件,簡單易?。
  9. 對于耗時敏感的核?業務,推薦使?最終?致的業務層柔性事務,數據庫層的 2PC 分布式事務耗時較?,性能較為?般,但是 2PC使?簡單,能滿??部分業務的使?。
  10. ?定要利?好分布式數據庫架構的特點,設計多活架構,每個機房都可以有寫?流量,提升資源使?率和業務連續性,請 DBA 和業務??起做好全鏈路的架構設計。
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,345評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,494評論 3 416
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,283評論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,953評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,714評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,186評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,255評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,410評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,940評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,776評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,976評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,518評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,210評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,642評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,878評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,654評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,958評論 2 373

推薦閱讀更多精彩內容