資料庫規範(收藏版)
目的: 優化資料訪問層程式碼
範圍: 公司 業務過程中使用的所有SQL程式碼
原則:
減少SQL效能問題導致的系統故障,提高系統的穩定性。
對於業務模組,由於產品形態或者其他原因,導致流量變更,尤其是巨大的流量增加,需要事先郵件通知到相關的DBA做風險評估。
MySQL設計標準(紅色的為必須遵守的)
-2、所有表的DDL,都不回退
-1、資料庫命名規範,統一:vip_xxxx;表名不超過40個字元(即最大隻能40個字元)
0、表一旦設計好,欄位只允許增加,不允許減少(drop column),不允許改名稱(change column)
1、統一使用INNODB儲存引擎,UTF8編碼
2、需在設計階段考慮如果訪問量非常大,且不做scale out表拆分的話,需讀寫分離,但讀寫分離注意主從複製有延遲的可能性; 參考 是否採用讀寫分離方案的討論
3、禁用Stored procedure (包括儲存過程,函式,觸發器);
4、禁止使用 UUID(),USER()這樣的MYSQL INSIDE函式對於複製來說是很危險的,會導致主備資料不一致,重要的是會嚴重影響mysql效能。
5、表必須有主鍵,建議統一由Auto-Increment欄位生成整型,不建議使用組合主鍵,自增id只作為虛擬主鍵,不建議與業務資料處理有關聯關係,如果把控不好,會有問題(案例:
為何要使用自增id作為pk
6、庫名、表名、欄位名、索引名必須使用小寫字母;
7、多表join的時候,寫SQL的時候一定要給每個欄位指定表名做字首;如: select a.id,a.name from test1 a, test2 b where a.id=b.id
8、如果應用使用的是長連線,應用必須具有自動重連的機制。但請避免每執行一個SQL去檢查一次DB可用性;
9、如果應用使用的是長連線,應用應該具有連線的TIMEOUT檢查機制,及時回收長時間沒有使用的連線,TIMEOUT時間一般建議為20min。
10、欄位的資料型別和長度,必須符合資料的實際, 不能濫用varchar來解決一切問題:
自增主鍵:bigint unsinged
狀態/code欄位:tinyint/int/varchar
日期時間:timestamp
日期:date
帶小數數值:decimal
整數數值:tinyint/int/bigint (unsigned)
字元:varchar
11、儘量用單表查詢,避免多表JOIN,禁止多於3表join,join的欄位資料型別必須絕對一致。
12、表名列名必須有註釋(必須加上COMMENT '<欄位扼要解說>'),表結構變更須由庫表OWNER所在團隊發起,
13、SQL語句必須採用preparedStatement技術, 如果程式語言不支援preparedStatement技術,需要做好特殊字元過濾,如不要前後有空串等
14、不要使用TEXT、BLOB、char,請使用VARCHAR(N),N表示的是字元數不是位元組數,比如VARCHAR(255),可以最大可儲存255個漢字,需要根據實際的寬度來選擇N,請注意同一表中,所有varchar欄位的長度加起來,不能大於65535。
15、每張表資料量建議控制在千萬級別行以下,為此設計階段需考慮資料的歸檔。
16、如果欄位只有true or false,請使用tinyint(數值範圍-128~127),如模組分類:1訂單 2商品;刪除標誌 0正常,1刪除;狀態 1為可選,2為不可選等等
17、儲存時間(精確到秒)建議使用TIMESTAMP型別,因為TIMESTAMP使用4位元組,DATETIME使用8個位元組,同時TIMESTAMP具有自動賦值以及自動更新的特性。
18、禁止default NULL,數字型別not null default 0,字元型別not null default '',時間not null default '1970-01-01 00:00:00'或者 ‘0000-00-00 00:00:00’;
為何要禁止NULL
19、所有表必須有create_time和last_update_time,方便後期資料分析與記錄變化排查,哪怕只是配置表,只有10行記錄; 為進一步明確操作來源,統一加上created_by, last_updated_by兩個欄位,記錄資料的建立者和修改者。
20、所有業務實體表/關係表,禁止硬刪除,必須軟刪除,加上is_deleted欄位,標註這條記錄的狀態。
21、加欄位禁止使用after,因為你不確定全域性程式碼裡面(如其他團隊使用你的表)是否都insert into table(col,col,col。。。) value,如果你在中間插一個欄位,就導致資料偏移的問題了,影響可大可小,同樣select * 的也可能會影響數值的偏移,所以才要求,禁止after,必須帶default(第18點要求)。
22、線上MySQL表名是忽略大小寫的(lower_case_table_names=1)
23、查詢欄位裡的值是忽略大小寫的(mysql儲存是區分大小寫,但查詢是忽略大小寫,如果業務查詢要區分大小寫,如short url 對應的long url,則select xxx from short_map where binary url = ‘’ )
大小寫的例子
24、表大小控制在3千萬行以內,控制行數:
(1)控制DDL變更時間;
(2)同時,縮短整庫備份時間,降低恢復難度;
(3)從效能角度看,走索引,每次查詢資料精準到就幾百行,22億和1億效能是沒有區別的,如果是比較特殊場景,如where後面不帶時間範圍條件,而你明知道n個月前的資料不需要,全表查某個型別(type)的查詢就有效能問題了。
錶行數 |
1千萬行 |
2千萬行 |
3千萬行 |
4千萬行 |
備註 |
DDL變更需要n小時 |
1 |
2 |
3 |
4 |
Online DDL的速度:總行數/3000行每秒/3600秒= n 小時,注意:一個庫要變更10張表,只能是序列,不能並行,假設每一張表都要1小時的話,那麼10張表變更完,就是10個小時
25、欄位size大小好合理設計,特別是字元型欄位
(1)效能考慮
(2)業務設計上,容易出現業務的突發情況導致資料長度超過欄位長度從而產生故障
事務的處理標準
1、禁止一些框架或定製化的底層類等使用set autocommit=0;set autocommit=1;這樣控制事務,應該由程式把控,需要時begin;操作完後及時commit;(曾經發生過的案例:一個session持有鎖,事務長時間不commit的場景)
2、要合理使用事務,例子:購物車如下的 事務處理,可以更好的優化。
購物車事務處理的例子
索引使用標準
1、非唯一索引建議使用“idx_表縮寫名稱_欄位縮寫名稱”進行命名。
2、唯一索引建議使用“uniq_表縮寫名稱_欄位縮寫名稱”進行命名。
3、索引名稱必須使用小寫。
4、唯一鍵不和主鍵重複。每個業務實體表和關係表都應該至少有一個業務主鍵對應的唯一索引。
5、索引欄位的順序需要考慮欄位值去重之後的個數,個數多的放在前面,就是資料分佈。
6、使用EXPLAIN判斷SQL語句是否合理使用索引,儘量避免extra列出現:Using File Sort,Using Temporary。
7、UPDATE、DELETE語句需要根據WHERE條件新增索引。
8、合理建立聯合索引(避免冗餘),(a,b,c) 相當於 (a) 、(a,b) 、(a,b,c)。
9、合理利用覆蓋索引。比如SELECT email,uid FROM user_email WHERE uid=xx,如果uid不是主鍵,適當時候可以將索引新增為index(uid,email),以獲得性能提升。
約束設計
a) 主鍵的內容不能被修改。
b) 外來鍵約束一般不在資料庫上建立,只表達一個邏輯的概念,由程式控制。
d) 禁用資料庫外來鍵
命名
a) 主鍵約束:預設PRIMARY;
b) unique約束:UK_<column_name>
c) check約束: CK_<column_name>
d) 外來鍵約束: 業務禁用
SQL語句標準
0、禁止多於2表的join。
1、使用prepared statement,可以提供效能並且避免SQL注入。
2、SELECT語句只獲取需要的欄位,禁止使用SELECT * FROM語句,這是有效防止新增欄位對應用邏輯的影響,還能減少對效能的影響;
3、INSERT語句必須顯式的指明欄位名稱,不使用INSERT INTO table value()。
4、禁止在where子句中對欄位施加函式,如to_date(add_time)>xxxxx,應改為:add_time >= unix_timestamp(date_add(str_to_date('20130227','%Y%m%d'),interval - 29 day))
5、UPDATE、DELETE語句不使用LIMIT 。以前我們使用的是MySQL 5.0,使用statment模式,所以有此規範,目前5.5,row和mixed模式不會出現,此規則去掉。
6、寫到應用程式裡的SQL語句,禁止一切DDL操作,如對這些許可權有要求,必需與DBA協商同意方可使用
7、WHERE條件中必須使用合適的型別,避免MySQL進行隱式型別轉化,如ISENDED=1,欄位型別是tinyint,那麼不能是ISENDED=‘1’。
8、避免在SQL語句進行數學運算或者函式運算,容易將業務邏輯和DB耦合在一起。
9、INSERT語句使用batch提交。
10、避免使用儲存過程、觸發器、函式等,容易將業務邏輯和DB耦合在一起,並且MySQL的儲存過程、觸發器、函式中存在一定的bug。
11、使用合理的SQL語句減少與資料庫的互動次數。
12、不使用ORDER BY RAND(),使用其他方法替換。
13、建議使用合理的分頁方式以提高分頁的效率。
14、InnoDB表避免使用COUNT(*)操作,計數統計實時要求較強可以使用memcache或者redis,非實時統計可以使用單獨統計表,定時更新。
15、不建議使用%字首模糊查詢,例如LIKE “%weibo”。
16、避免多餘的排序。使用GROUP BY 時,預設會進行排序,當你不需要排序時,可以使用order by null,例如Select a.OwnerUserID,count(*) cnt from DP_MessageList a group by a.OwnerUserID order by null;
17、新增排序要求:不鼓勵在DB裡排序,特別是只有1000行一下的,請在app server上排序,app server有上百臺,而db僅僅個位數的伺服器數量,排序都在db,會把db壓垮的,特別是禁止上千行的排序在db這邊。
18、 禁止使用 REPLACE INTO ;
使用replace帶來的問題
19、禁止使用子查詢,select col、col from table where id in (select col from table)這是禁止的;
20、batch size 大小不能超過1000,同時請根據業務QPS和記錄長度來評估1000以內什麼值合適,如where col in ()的值不能超過1000。參考:batch size標準
每個整數型別的儲存和範圍。
型別 |
位元組 |
最小值 |
最大值 |
(帶符號的/無符號的) |
(帶符號的/無符號的) |
||
TINYINT |
1 |
-128 |
127 |
TINYINT unsigned |
0 |
255 |
|
SMALLINT |
2 |
-32768 |
32767 |
SMALLINT unsigned |
0 |
65535 |
|
MEDIUMINT |
3 |
-8388608 |
8388607 |
MEDIUMINT unsigned |
0 |
16777215 |
|
INT |
4 |
-2147483648 |
2147483647 |
INT unsigned |
0 |
4294967295 |
|
BIGINT |
8 |
-9223372036854775808 |
9223372036854775807 |
BIGINT unsigned |
0 |
18446744073709551615 |
DECIMAL(
M
,D
) 最大 DECIMAL(65,30),其中對應下表的佔用位元組數
Leftover Digits |
Number of Bytes |
---|---|
0 | 0 |
1–2 | 1 |
3–4 | 2 |
5–6 | 3 |
7–9 | 4 |