流程規(guī)范
- 服務(wù)正式上線前旭斥,一定要check下線上數(shù)據(jù)庫改動已經(jīng)上線悬包。不然會引起線上事故谊却!
- 測試環(huán)境也要有,不然測試環(huán)境后面會越來越難建設(shè)哑芹。
設(shè)計規(guī)范
規(guī)范共分為【強制】炎辨、【推薦】、【參考】三個等級聪姿,優(yōu)先級從高至低碴萧,如違反【強制】規(guī)范Reviewer應(yīng)當(dāng)打回。
命名規(guī)范
【強制】表的命名最好是“業(yè)務(wù)名稱_表的作用” 末购,無需額外的數(shù)據(jù)庫名開頭破喻。
對于bits數(shù)據(jù)庫來說,正例:app_build_config盟榴,workflow_job曹质; 反例:bits_app_info,bits_xxx擎场;
【強制】表名羽德、字段名必須使用小寫字母或****數(shù)字,禁止出現(xiàn)數(shù)字開頭迅办,禁止兩個下劃線中間只 出現(xiàn)數(shù)字宅静。正例:app_user,app_config站欺,level3_name 反例:AppUser姨夹,rdcConfig,level_3_name
【強制】表名使用單數(shù)矾策。 說明:表名應(yīng)該僅僅表示表里面的實體內(nèi)容磷账,不應(yīng)該表示實體數(shù)量,符合表達習(xí)慣贾虽。
【強制】索引名字的前綴必須為 idx_
够颠,唯一約束/索引的前綴必須為 uk_
【強制】所有命名必須使用全名,有默認約定的除外榄鉴,如果超過 30
個字符履磨,使用縮寫
【強制】每個字段和表必須提供清晰的注釋蛉抓,現(xiàn)在沒有注釋 rds 不讓建表。
【推薦】庫的命名最好是“系統(tǒng)名稱_子系統(tǒng)名”剃诅, 正例 bits_pipeline巷送,bits_ttp。
字段規(guī)范
【強制】必加的三個字段
- id(unsigned bigint)矛辕,單表時自增笑跛。防止枚舉時用id generator生成
- create_time(timestamp),用于記錄主動創(chuàng)建時間聊品,默認值:current_timestamp
- update_time(timestamp)飞蹂,用于記錄最后更新時間,默認值: current_timestamp
【強制】每個表不超過30個字段
【強制】如果存儲的字符串長度固定翻屈,使用 char 定長字符串類型
【強制】文本數(shù)據(jù)盡量用varchar 陈哑,變長存儲,更省存儲空間伸眶,長度不要超過 2700惊窖,如果存儲長 度大于此值,定義字段類型為 text厘贼,獨立出來一張表界酒,用主鍵來對應(yīng),避免影響其它字段索引效率嘴秸。
【強制】存儲字段如果為非負數(shù)毁欣,默認加上UNSIGNED。
【強制】小數(shù)類型為 decimal岳掐,禁止使用 float 和 double署辉。decimal不存在精度損失,數(shù)據(jù)類型decimal(p,s) 需要分別指定小數(shù)的最大位數(shù)(p)和小數(shù)位的數(shù)量(s)岩四。
【強制】如果修改字段含義或?qū)ψ侄伪硎镜臓顟B(tài)追加時哭尝,需要及時更新字段注釋。
【強制】業(yè)務(wù)中選擇性很少的狀態(tài)剖煌、****類型 等字段推薦使用 TINYINT
材鹦。從1開始枚舉,并且將具體含義寫在字段備注里耕姊。禁止使用 bool 來存狀態(tài)桶唐。
【強制】索引字段不要設(shè)置為null
【強制】優(yōu)先選擇utf8mb4
字符集,它的兼容性最好茉兰,而且還支持emoji字符尤泽。
【推薦】json 的數(shù)據(jù)使用 json 類型存儲而不是 text。MySQL 會幫我們做 json 格式校驗。注意長度限制坯约。(看業(yè)務(wù)場景)
【推薦】字段盡量設(shè)置為 NOT NULL熊咽, 為字段提供默認值。 如字符型的默認值為一個空字符串值闹丐;數(shù)值型默認值為數(shù)值 0横殴;邏輯型的默認值為數(shù)值 0;
【推薦】考慮業(yè)務(wù)場景,選擇軟硬刪除卿拴,必要的話衫仑,新增 delete_at,軟刪除堕花。
ToDo:補 GORM 和 build Tools @劉思齊 @劉濤
注意點:
count(*) 會統(tǒng)計NULL的行文狱, count(列名) 不會統(tǒng)計此列為NULL值的行
count(distinct col) 計算該列除 NULL 之外的不重復(fù)行數(shù),注意 count(distinct col1, col2) 如果其中一列全為 NULL缘挽,那么即使另一列有不同的值瞄崇,也返回為 0。
【推薦】遇到BLOB到踏、TEXT字段,則盡量拆出去尚猿,再用主鍵做關(guān)聯(lián)窝稿。超過4k大值拆出,考慮換存儲方案凿掂,主從延遲風(fēng)險伴榔。
【推薦】反范式設(shè)計:字段允許適當(dāng)冗余,以提高查詢性能庄萎。 如app表中的應(yīng)用名稱/應(yīng)用id等唯一鍵踪少,可以在別的業(yè)務(wù)表中均存儲一份,避免使用 join糠涛。
【推薦】時間類型采用為 timestamp
數(shù)據(jù)類型援奢,因為 datetime
占用 8 字節(jié),timestamp
僅占用4字節(jié)忍捡,范圍為 1970-01-01 00:00:01
到2038-01-01 00:00:00
【參考】合適的字符存儲長度集漾,不但節(jié)約數(shù)據(jù)庫表空間、節(jié)約索引存儲砸脊,更重要的是提升檢索速度具篇。
類型(同義詞) | 存儲長度(BYTES) | 最小值(SIGNED/UNSIGNED) | 最大值(SIGNED/UNSIGNED) |
---|---|---|---|
整型數(shù)字 | |||
TINYINT | 1 | -128/0 | 127/255 |
SMALLINT | 2 | -32,768/0 | 32767/65,535 |
MEDIUMINT | 3 | -8,388,608/0 | 8388607/16,777,215/ |
INT(INTEGER) | 4 | -2,14,7483,648/0 | 2147483647/4,294,967,295/ |
BIGINT | 8 | -2^63/0 | 263-1/264-1 |
時間類型 | |||
DATETIME | 8 | 1001-01-01 00:00:00 | 9999-12-31 23:59:59 |
DATE | 3 | 1001-01-01 | 9999-12-31 |
TIME | 3 | 00:00:00 | 23:59:59 |
YEAR | 1 | 1001 | 9999 |
TIMESTAMP | 4 | 1970-01-01 00:00:00 | 2038-01-01 00:00:00 |
索引規(guī)范
【強制】表寫多讀少建議索引數(shù)不超過5個。表讀多寫少索引數(shù)不超過10個凌埂。
【強制】業(yè)務(wù)上具有唯一特性的字段驱显,即使是多個字段的組合,也必須建成唯一索引。
【強制】建議不使用 join埃疫。如果一定要使用伏恐,最多兩個表 join。需要 join 的字段熔恢,數(shù)據(jù)類型必須絕對一致;多表關(guān)聯(lián)查詢時脐湾, 保證被關(guān)聯(lián)的字段需要有索引。
【強制】在 varchar 字段上建立索引時叙淌,必須指定索引長度秤掌,沒必要對全字段建立索引,根據(jù) 實際文本區(qū)分度決定索引長度即可鹰霍。 說明:索引的長度與區(qū)分度是一對矛盾體闻鉴,一般對字符串類型數(shù)據(jù),長度為 20 的索引茂洒,區(qū)分度會高達 90%以上孟岛。
【強制】在建立索引時,多考慮建立聯(lián)合索引督勺,并把區(qū)分度最高的字段放在最前面渠羞。
【強制】模糊查詢僅允許右模糊 即“xxx%”,如果需要請走ES來解決智哀。
【強制】不要用外鍵次询,一切外鍵概念在應(yīng)用層解決。
【推薦】利用覆蓋索引來進行查詢操作瓷叫,避免回表屯吊。即 select b from tableA where a = 1
【推薦】如果選擇性超過 20%(區(qū)分度低于20%),那么全表掃描比使用索引性能更優(yōu)摹菠,即沒有設(shè)置索引的必要盒卸。
【推薦】防止因字段類型不同造成的隱式轉(zhuǎn)換,導(dǎo)致索引失效次氨。
【推薦】如果有 order by 的場景蔽介,請注意利用索引的有序性。order by 最后的字段是組合索引的一部分煮寡,并且放在索引組合順序的最后屉佳,避免出現(xiàn) file_sort 的情況,影響查詢性能洲押。
【推薦】不要對頻繁更新字段加索引
【參考】創(chuàng)建索引時避免有如下極端誤解
錯誤一:寧濫勿缺武花。認為一個查詢就需要建一個索引。
錯誤二:寧缺勿濫杈帐。認為索引會消耗空間体箕、嚴重拖慢更新和新增速度专钉。
錯誤三:抵制惟一索引。認為業(yè)務(wù)的惟一性一律需要在應(yīng)用層通過“先查后插”方式解決累铅。
使用規(guī)范
【強制】用Explain來做SQL 性能優(yōu)化跃须,type 至少要達到 range 級別,要求是 ref 級別娃兽,如果可以是 consts 最好
值 | 描述 |
---|---|
system | The table has only one row (= system table). This is a special case of the const join type. |
const | The table has at most one matching row, which is read at the start of the query. Because there is only one row, values from the column in this row can be regarded as constants by the rest of the optimizer. const tables are very fast because they are read only once. |
eq_ref | One row is read from this table for each combination of rows from the previous tables. Other than the system and const types, this is the best possible join type. It is used when all parts of an index are used by the join and the index is a PRIMARY KEY or UNIQUE NOT NULL index. |
ref | All rows with matching index values are read from this table for each combination of rows from the previous tables. ref is used if the join uses only a leftmost prefix of the key or if the key is not a PRIMARY KEY or UNIQUE index (in other words, if the join cannot select a single row based on the key value). If the key that is used matches only a few rows, this is a good join type. |
fulltext | The join is performed using a FULLTEXT index. |
ref_or_null | This join type is like ref, but with the addition that MySQL does an extra search for rows that contain NULL values. This join type optimization is used most often in resolving subqueries. In the following examples, MySQL can use a ref_or_null join to process ref_table: |
index_merge | This join type indicates that the Index Merge optimization is used. In this case, the key column in the output row contains a list of indexes used, and key_len contains a list of the longest key parts for the indexes used |
unique_subquery | This type replaces eq_ref for some IN subqueries of the following form: |
index_subquery | This join type is similar to unique_subquery. It replaces IN subqueries, but it works for nonunique indexes in subqueries of the following form |
range | Only rows that are in a given range are retrieved, using an index to select the rows. The key column in the output row indicates which index is used. The key_len contains the longest key part that was used. The ref column is NULL for this type. |
index | The index join type is the same as ALL, except that the index tree is scanned. This occurs two ways: - If the index is a covering index for the queries and can be used to satisfy all data required from the table, only the index tree is scanned. In this case, the Extra column says Using index. An index-only scan usually is faster than ALL because the size of the index usually is smaller than the table data. -A full table scan is performed using reads from the index to look up data rows in index order. Uses index does not appear in the Extra column. -MySQL can use this join type when the query uses only columns that are part of a single index. |
ALL | A full table scan is done for each combination of rows from the previous tables. This is normally not good if the table is the first table not marked const, and usually very bad in all other cases. Normally, you can avoid ALL by adding indexes that enable row retrieval from the table based on constant values or column values from earlier tables. |
【強制】不要使用 count(列名)或 count(常量)來替代 count()菇民,count()是 SQL92 定義的 標準統(tǒng)計行數(shù)的語法,跟數(shù)據(jù)庫無關(guān)投储,跟 NULL 和非 NULL 無關(guān)第练。
- count(*)會統(tǒng)計值為 NULL 的行,而 count(列名)不會統(tǒng)計此列為 NULL 值的行玛荞。
【強制】連表查詢的情況下娇掏,要確保關(guān)聯(lián)條件的數(shù)據(jù)類型一致,避免嵌套
【強制】使用 ISNULL()來判斷是否為 NULL 值勋眯。 說明:NULL 與任何值的直接比較都為 NULL婴梧。
NULL<>NULL的返回結(jié)果是NULL,而不是false客蹋。
NULL=NULL的返回結(jié)果是NULL塞蹭,而不是true。
NULL<>1的返回結(jié)果是NULL讶坯,而不是true番电。
【強制】查詢時不要對索引列做計算/使用函數(shù)。避免索引列的類型轉(zhuǎn)換以及字符串編碼轉(zhuǎn)換闽巩。
【強制】更新數(shù)據(jù)超過1000條的update钧舌,delete和insert語句担汤,需要改成分批量多次更新來進行
【強制】禁止使用存儲過程涎跨,視圖,觸發(fā)器崭歧,event等高級特性
【強制】in子句中的值不要超過500個
【強制】提RDS DML工單時隅很,update or delete 的長度確定時,加上 limit <行數(shù)>率碾。這樣就算提的工單 DML SQL寫錯了叔营,錯誤造成的影響也可控。(降低工單造成的事故風(fēng)險)
【強制】提工單的時候備注背景寫一下所宰。
【推薦】不建議使用子查詢绒尊,建議將子查詢 SQL 拆開結(jié)合程序多次查詢。
【推薦】不要對索引字段使用負向查詢仔粥,比如not in,not like