1. 程式人生 > 實用技巧 >mysql操作規範

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代替FLOATDOUBLE儲存精確浮點數。例如與錢相關的資料。

  • 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、禁止在資料庫中儲存明文密碼。

採用加密字串儲存密碼,並保證密碼不可解密,同時採用隨機字串加鹽保證密碼安全。防止資料庫資料被公司內部人員或黑客獲取後,採用字典攻擊等方式暴力破解使用者密碼。