mysql操作規範
mysql操作規範
1. 遵循約定
1.1文件適用範圍
該文件可以用於開發人員表結構設計及DBA人員做Schema Review約束使用。
1.2 MySQL特點
-
mysql是單程序多執行緒,不像Oracle那樣是多程序的。
-
每個mysql內部執行緒同時只能用到一個邏輯cpu執行緒。
-
每個SQL同時只能用到一個邏輯CPU執行緒。
-
無執行計劃快取(無類似ORACLE的library cache),不過MySQL的執行計劃解析比較輕量級,效率還不錯,這方面不會是瓶頸。
-
沒有thread pool時,如果有瞬間大量連線請求,效能會急劇下降。
2. 開發規範
2.1 命名規範
-
庫名、表名、欄位名必須使用小寫字母,並採用下劃線分割
-
庫名、表名、欄位名禁止查過32個字元
-
庫名、表名、欄位名必須見名知意,命名與業務產品線等相關聯
-
庫名、表名、欄位名禁止使用MySQL關鍵字
-
臨時庫、表名必須以tmp為字首,並以日期為字尾,例如tmp_api_20190103。
-
備份庫、表名必須以bak為字首,並以日期為字尾,例如bak_api_20190103。
2.2 基礎規範
-
所有業務表使用InnoDB儲存引擎。
-
建立庫和表的字符集和排序規則設定為DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
-
所有表都需要添加註釋;除主鍵外的其他欄位都需要添加註釋。推薦採用英文標點,避免出現亂碼。
-
禁止在資料庫中儲存圖片、檔案等大資料。
-
每張表資料量控制在5000W以內。
-
禁止在線上做資料庫壓力測試。
-
禁止從開發、測試環境直連資料庫。
-
禁止開發人員操作線上資料庫。
2.3 庫表設計
2.3.1 總體原則
-
禁止使用分割槽表
-
將大欄位、訪問頻率低的欄位拆分到單獨的表儲存,分離冷熱資料。
-
推薦使用HASP進行水平拆分,表名字尾使用十進位制數,數字必須從0開始。例如將member_api進行按hash散表,member_api_0,...member_api_31。
2.3.2 主鍵設計
-
建議使用UNSIGNED INT/UNSIGNED BIGINT並且自增做為主鍵,順序insert效率更高,表空間碎片率更低
-
主鍵避免採用字元型,如VARCHAR/CHAR/UUID,會導致原本可以順序寫入的請求變成隨機寫入,效率更低。
-
表拆分時如果需要全域性唯一主鍵,可以採用發號器服務、redis全域性自增,或某個全域性DB裡統一分配等多種方式生成全域性唯一值。
2.3.4 欄位設計
-
建議為每個欄位設定為NOT NULL屬性,並設個預設值,可以減少儲存開銷及避免索引失效問題。同時為了避免程式寫入失敗,還可以增加預設值。
-
建議使用UNSIGNED儲存非負數值
-
用DECIMAL代替FLOAT和DOUBLE儲存精確浮點數。例如與錢相關的資料。
-
INT型別固定佔用4位元組儲存,例如INT(4)僅僅代表顯示字元寬度為4位,儲存長度。
-
強烈建議使用TINYINT型別來代替ENUM型別。ENUM型別在需要修改或增加列舉值時,需要線上DDL,成本較高;ENUM型別如果含有數字型別,可能會引起預設值混淆。
-
不到萬不得已不允許使用TEXT、BLOB資料型別。如果非要使用這種欄位型別,考慮單獨建一表,每次取資料利用主鍵只取1行,不要把TEXT、BLOB型別欄位列表顯示,TEXT、BLOB欄位型別在排序時會產生大量IO消耗。
-
禁止在資料庫中儲存明文密碼。
-
使⽤VARBINARY儲存大小寫敏感的變長字串或二進位制內容
-
使用盡可能小的VARCHAR欄位。VARCHAR(N)中的N表示字元數而非位元組數。
-
區分使用DATETIME和TIMESTAMP。儲存年使用YEAR型別。儲存日期使用DATE型別,儲存時間(精確到秒)建議使用TMISTAMP。
-
'建立時間',建議使用:
created_at
timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '建立時間' -
'更新時間',建議使用:
updated_at
timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新時間' -
禁止往mysql時間型別的欄位中傳'0000-00-00 00:00:00' 這種值。date 型別預設值建議設為 '1001-01-01',datetime 型別預設值建議設為'1001-01-01 00:00:00',timestamp 型別預設值建議設為'1971-01-01 00:00:00'
2.3.5 索引設計
-
單表中索引數量不建議超過5個。SSD硬碟現在可以稍微放寬限制。
-
單個索引中的欄位數不建議超過5個。
-
索引名必須全部使用小寫。
-
唯一索引按照uniq_欄位名進行命名。例如uniq_name。
-
非唯一索引按照idx_欄位名稱進行命名,例如idx_member_id。
-
組合索引建議包含所有欄位名,過長欄位名可以採用縮寫格式,如idx_age_name_add。
-
禁止冗餘索引。
-
禁止重複索引。
-
禁止使用外來鍵。
-
聯表查詢時,JOIN列的資料型別必須相同,並且要建索引。
-
不要在低基數列上建立索引,例如性別。
-
選擇區分度大的列建立索引。組合索引中,區分度大的欄位放在最前面。
-
對字串使用字首索引,字首索引長度不超過8個字元。
-
不要對過長的VARCHAR欄位建立索引。優先考慮字首索引。或新增CRC32 CRC64或MD5偽列並建立索引。例:SELECT * FROM urls WHERE url = 'http://wwww.shopperplus.com'; 建立crc_url偽列,程式使用時對url取其crc32值,然後select * from urls where crc_url=1215054814
-
合理建立聯合索引,(a,b,c)相當於(a)、(a,b)、(a,b,c)三個索引
-
合理使用覆蓋索引減少IO,避免排序。
-
使用prepared statement,可以提升效能並避免SQL注入。
-
用IN代替OR。SQL語句中的IN包含的值不應過多,應該少於1000個。
-
禁止隱式轉換。數值型別禁止加引號;字串型別必須加引號。表進行join時,如果表或者庫的字符集不一致也會發生隱式轉換。
-
避免使用CROSS JOIN和子查詢,必要時推薦使用JOIN代替子查詢。
-
避免在MySQL中進行數學運算和函式運算。比如select format(mydate,’yyyy-mm-dd’)。
2.4 SQL設計
-
使用prepared statement,可以提升效能並避免SQL注入。
-
用IN代替OR。SQL語句中的IN包含的值不應過多,應該少於1000個。
-
禁止隱式轉換。數值型別禁止加引號;字串型別必須加引號。表進行join時,如果表或者庫的字符集不一致也會發生隱式轉換。
-
避免使用CROSS JOIN和子查詢,必要時推薦使用JOIN代替子查詢。
-
避免在MySQL中進行數學運算和函式運算。比如select format(mydate,’yyyy-mm-dd’)。MySQL新版本有所改善,MySQL5.7可以使用虛擬列,MySQL8.0開始支援函式索引。
-
減少與資料庫互動次數,儘量採用批量SQL語句。使用下面的語句來減少和db的互動次數:
INSERT ...ON DUPLICATE KEY UPDATE
REPLACE INTO
INSERT IGNORE
INSERT INTO VALUES()
- 避免在MySQL中進行數學運算和函式運算。
- 拆分複雜SQL為多個小SQL,避免大事務。簡單SQL可以減少鎖表時間,可以有效的使用多核CPU。
- 獲取大量資料時,建議分批次獲取資料,每次獲取資料少於2000條,結果集應小於1M.
- 在業務邏輯準確的情況下使用UNION ALL 代替UNION
- 統計行數用COUNT(*)。
- SELECT只獲取必要的欄位,禁止使用SELECT *。
- SQL中避免使用now()、rand()、sysdate()、current_user()等結果不確定的函式。
- INSERT語句必須指定欄位列表,禁止使用INSERT INTO TABLE()。
- 禁止單條SQL語句同時更新多個表。
- 禁止在update時將”,”寫成”and”,非常非常非常危險。
正確示例:
```mysql
update table set uid=uid+1000,gid=gid+100 where id<=2;
錯誤示例:
update table set uid=uid+1000 and gid=gid+1000 where id<=2;
此時uid=uid+1000 and gid=gid+1000將作為值(1或0)賦值給uid,並且無warning!
-
禁止使用儲存過程、觸發器、檢視、自定義函式等。
-
建議使用合理的分頁方式以提高分頁效率。
-
提交線上建表改避免使用儲存過程、觸發器、檢視、自定義函式等。
-
禁止在業務從庫上執行後臺管理和統計類功能的QUERY,必要時為統計類單獨建從庫。
-
程式應有捕獲SQL異常的處理機制,必要時通過ROLLBACK顯式回滾。
-
重要SQL必須建索引:update、delete的where條件列、order by、group by、distinct欄位,奪標join欄位。
-
禁止使用%前導查詢,例如: like ’%’,無法利用到索引。o
-
禁止使用負向查詢,例如not in、!=、not like 。
-
使用EXPLAIN判斷SQL語句是否合理利用索引,儘量避免extra列出現:Using File sort、Using Temporary。
-
禁止使用order by rand()。
-
多使用等值操作,少使用或不使用非等值操作。WHERE條件中的非等值(IN、BETWEEN、<、<=、>、>=)會導致後面的條件使用不了索引,因為不能同時用到兩個索引條件。
-
表的連線條件最好是查詢結果集最少的為驅動表,後續表要有良好的索引
-
limit N以及limit M,N場景中,不管是M還是N的值都不宜過大(一般不超過一萬)。當M(起始值)較大時,建議用延遲關聯的方式優化,例如:
select * from (select * from t1 where id>(select id from t1 order by id desc limit 935510,1) limit 10) t order by id desc或select * from t1 inner join (select id from t1 order by id desc limit 935510,1) t2 using (id)
3. 行為規範
-
表結構變更必須通知DBA進行稽核。
-
禁止有SUPER、DDL、DCL許可權的應用賬號存在。
-
重大專案的資料庫方案選型和設計必須提前通知DBA參與。
-
批量匯入、匯出資料必須通知DBA稽核,並在執行過程中觀察服務。
-
批量更新資料,如UPDATE、DELETE操作,必須通知DBA稽核,並在執行過程中觀察服務。
-
產品出現非資料庫導致故障時,如被攻擊,必須及時通知DBA,便於維護服務穩定。
-
業務部門推廣活動或上線新功能,必須提前通知DBA進行服務和訪問量評估,並流出必要時間以便DBA完成擴容。
-
出現業務部門認為誤操作導致資料丟失,需要恢復資料的,必須第一時間通知DBA,並提供準確時間點、誤操作語句等重要線索。
-
提交線上建表該表需求,必須詳細註明涉及到的所有SQL語句(包括INSERT、DELETE、UPDATE、SELECT),便於DBA進行稽核和優化。
-
對同一個表的多次ALTER操作必須合併為一次。
-
不要在資料庫中存放業務邏輯。
4. FAQ
1、庫名、表名、欄位名禁止超過32個字元。
庫名、表名、欄位名支援最多64個字元,但為了同意規範、易於辨識以及減少傳輸量,禁止超過32個字元。
2、庫名、表名、欄位名禁止使用MySQL關鍵字。
當庫名、表名、欄位名等屬性含有保留字時,SQL語句必須使用反引號引用屬性名稱,這將使得SQL語句書寫、SHELL指令碼中的變數的轉義變得非常複雜。【MySQL關鍵字】
3、禁止在資料庫中儲存明文密碼。
採用加密字串儲存密碼,並保證密碼不可解密,同時採用隨機字串加鹽保證密碼安全。防止資料庫資料被公司內部人員或黑客獲取後,採用字典攻擊等方式暴力破解使用者密碼。