現總結工作與學習中關於資料庫的規範設計與優化技巧

1.規範背景與目的

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

2.設計規範

2.1資料庫設計

說明:

以下所有規範會按照【高危】、【強制】、【建議】三個級別進行標註,遵守優先順序從高到低。

對於不滿足【高危】和【強制】兩個級別的設計,強制要求修改。

禁止使用的sql語句

【高危】

  • 禁用update | delete t1 ... where a = xx limit xxx; 這種帶limit的更新語句。因為會導致主從不一致,導致資料錯亂。
  • 禁止使用關聯子查詢,如update t1 set ... where name in(select...) 效率極其低下
  • 禁用procedure、function\trigger\views\ event\外來鍵約束。因為他們消耗資料庫資源,降低資料庫例項可擴充套件性。推薦都在程式端實現。
  • 禁用insert into .. on duplicate key update.. 在高併發環境下,會造成主從不一致。
  • 禁止連表更新語句

2.1.1 庫的建立

  • 【強制】庫的名稱必須控制在32個字元以內,庫名稱格式:業務系統名稱_子系統名稱,同一模組使用的表名儘量使用統一字首
  • 【強制】建立資料庫時必須顯式指定字符集,並且字符集只能是utf8或者utf8mb4.建立資料庫SQL舉例:create database db1 default character set utf8;

2.1.2 表結構

  1. 【強制】
  • 對於關係表,表名和表名之間儘量體現join的關係,如user和group表直接的關係:x_user_group_xx
  • 表和列的名稱必須控制在32個字元以內,表名只能使用字母、數字和下劃線,一律小寫
  • 表名與模組名強相關,如渠道系統採用“qd_”作為字首
  • 建立表時必須顯式指定字符集為utf8或者utf8mb4
  • 建立表時必須顯式指定表儲存引擎型別,如無特殊需求,一律為InnoDB。當需要使用除InnoDB/MyISAM/Memory以外的儲存引擎時,必須通過稽核才能使用。(InnoDB表支援事務、行鎖、宕機恢復、MVCC等關係型資料庫重要特性,為業界使用最多的MySQL引擎。)
  • 建表必須使用comment 添加註釋
  • 中間表用於保留中間結果集,名稱必須以tmp_開頭。備份表名稱必須以bak_開頭。
  • 對於超過100W行的資料量大的表進行alter table時,必須在業務低峰期執行。因為alter table 會產生表鎖,期間會阻塞對於該表的所有寫入,對於業務可能會產生極大影響
  1. 【建議】
  • 核心表須有建立時間和更新時間
  • 表中所有欄位加上NOT NULL屬性,業務可以根據需要定義DEFAULT值。因為使用NULL值會存在每一行都佔用額外儲存空間、資料遷移容易出錯、聚合函式計算結果偏差等問題
  • 表中有blob、text等大欄位,垂直查分到其他表中。
  • 有些欄位可以適當冗餘,比如user_name,不僅僅存user_id,減少join查詢

2.1.3 列資料型別優化

  1. 【建議】
  • 表中資料值很少的狀態status、型別type等欄位使用tinytint或者smallint型別節省儲存空間
  • 不推薦使用blob,text等型別,比較浪費硬碟和記憶體空間。
  • 文字資料儘量用varchar儲存。因為varchar是變長儲存,比char更省空間。一般verchar型別欄位樹不要超過2700.
  • 時間型別儘量選取timestamp。因為datetime佔用8個位元組,timestamp佔用4個位元組

2.1.4 索引設計

  1. 【強制】
  • InnoDB表必須主鍵為id int/bigint auto_increment,且主鍵禁止被更新。
  • 單個索引中每個索引記錄的長度不能超過64k
  1. 【建議】
  • 主鍵的索引以“pk_”開頭,唯一索引以“uk_”開頭,普通索引以“idx_”開頭,一律使用小寫格式,以表名/欄位的名稱或縮寫作為字尾。
  • 單個表的索引個數不能超過7個
  • 在建立索引時,多考慮建立聯合索引,並把區分度最高的欄位方在前面。
  • 在多表關聯的join中,保證驅動表的連線列上有索引,這樣join執行效率會更高
  • 保證表中互相不存在冗餘的索引。

2.1.5 程式DAO設計

1.【建議】

  • 對於log或者history型別的表,歲時間增長容易越來越大,因此上線錢DBA必須簡歷表資料清理或歸檔方案。
  • 主表機芯增、刪、改,從表進行查操作
  • 對於單表讀寫比大於10:1的資料行或單個列,可以將熱點資料放在快取裡(如mechache或redis),加快訪問速度,降低MySQL壓力。

2.1.6 規範的建表語句示例

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 '使用者提交的簡歷存放地址',
`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沒有來得及更新的話,系統會報錯。—————sql優化
  • where條件裡等號左右欄位型別必須一致,否則無法利用索引—————sql優化
  1. 【建議】
  • insert into ... value(xxx), (xxx) ...這裡的(xxx)不要超過5000個。值過多雖然上線很快,但會引起主從同步延遲。
  • select語句中不要使用union,推薦使用union all,並且union子句個數限制在5個以內。因為union all不需要去重,節省資料庫資源,提高效能。—————sql優化
  • in值列表限制在500以內,這麼做事圍欄減少底層掃描,減輕資料庫壓力從而加速查詢
  • 事務裡批量更新資料需要控制數量,進行必要的sleep,做到少量多次。—————優化
  • where子句中儘量少使用圈模糊的like條件查詢,必須有其他等值或範圍查詢條件,否則無法利用索引—————sql優化
  • 索引列不要使用函式或表示式,否則無法利用索引****—————sql優化
  • 分頁查詢,當limit起點較高時,可先用過濾條件進行過濾。如select a,b,c from t1 limit 10000,20;優化為: select a,b,c from t1 where id>10000 limit 20;

2.2.1 多表連線

  1. 【強制】
  • 禁止跨db的join語句,因為這樣可以減少模組間的耦合,為資料庫拆分奠定堅實的基礎
  • 禁止在業務的更新類SQL語句中使用join,比如update t1 join t2..
  1. 【建議】
  • 不建議使用子查詢,建議將子查詢SQL拆開結合程式多次查詢,或使用join來代替子查詢—————sql優化
  • 線上環境,多表join不要超過3個表
  • 多表連線查詢推薦使用別名,且SELECT列表中要用別名引用欄位
  • 在多表join中,儘量選取結果集較小的表作為驅動表,來join其他表

2.2.3 事務

  1. 【強制】
  • 程式設計必須考慮“資料庫事務隔離級別”帶來的影響,包括髒讀、不可重複度和幻讀。線上建議事務隔離級別為repeatable-read
  1. 【建議】
  • 事務中INSERT | UPDATE | DELETE | REPLACE 語句操作的行數控制在2000以內,以及WHERE子句中IN列表的傳參個數控制在500以內。
  • 批量操作是,需要控制事務處理隔離時間,進行必要的sleep,一般建議值5-10秒
  • 對於有auto_increment屬性欄位的表的插入操作,併發需要控制在200以內
  • 事務裡包含SQL不超過5個(支付業務除外)。因為過長的事務會導致鎖資料較久,MySQL內部快取、連線消耗過多等雪崩問題。
  • 對於MySQL主從延遲嚴格敏感的select語句,請開啟事務強制訪問主庫

2.2.4 排序和分組

  1. 【建議】
  • 減少使用order by,和業務溝通能不排序就不排序,或將排序放到程式端去做。order by、group by、distinct 這些語句較為耗費CPU,資料庫的CPU資源是極其寶貴的。—————sql優化
  • order by、group by、distinct這些SQL儘量利用索引直接檢索出排序好的資料。如where a=1 order by可以利用key(a,b)。
  • 包含了order by、group by、distinct這些查詢的語句,where條件過濾出來的結果集請保持在1000行以內,否則SQL會很慢。

不斷積累中,未完待續...


若覺得博文不錯或對你有幫助,請點選【推薦】或者【關注】,感謝你的支援