值得收藏,超全的資料庫建表SQL索引規範

值得收藏,超全的資料庫建表/SQL/索引規範

juejin。im/post/6871969929365553165

| 來源上海資料分析 | 轉自

因為工作崗位的原因,負責制定了關於後端組資料庫的規約規範,作為所有產品線的規範,歷經幾版的修改,最終形成下邊的文字,規範在整個後端執行也有大半年的時間,對於整個團隊在開發階段就減少不恰當的

建表語句、錯誤SQL、錯誤的索引有積極的意義

,故分享出來給大家參考。

下邊分為建表規約、SQL規約、索引規約三個部分,每部分的每一條都有強制、建議兩個級別,大家在參考時,根據自己公司的情況來權衡。

1

建表規約

【強制】(1) 儲存引擎必須使用InnoDB

解讀:InnoDB支援事物、行級鎖、併發效能更好,CPU及記憶體快取頁最佳化使得資源利用率更高。

【強制】(2)每張表必須設定一個主鍵ID,且這個主鍵ID使用自增主鍵(在滿足需要的情況下儘量短),除非在分庫分表環境下。

解讀:由於InnoDB組織資料的方式決定了需要有一個主鍵,而且若是這個主鍵ID是單調遞增的可以有效提高插入的效能,避免過多的頁分裂、減少表碎片提高空間的使用率。而在分庫分表環境下,則需要統一來分配各個表中的主鍵值,從而避免整個邏輯表中主鍵重複。

【強制】(3)必須使用utf8mb4字符集

解讀:在Mysql中的UTF-8並非“真正的UTF-8”,而utf8mb4”才是真正的“UTF-8”。

【強制】(4) 資料庫表、表字段必須加入中文註釋

解讀:大家都別懶

【強制】(5) 庫名、表名、欄位名均小寫,下劃線風格,不超過32個字元,必須見名知意,禁止拼音英文混用。

解讀:約定

【強制】(6)單表列數目必須小於30,若超過則應該考慮將表拆分

解讀:單表列數太多使得Mysql伺服器處理InnoDB返回資料之間的對映成本太高

【強制】(7)禁止使用外來鍵,如果有外來鍵完整性約束,需要應用程式控制

解讀:外來鍵會導致表與表之間耦合,UPDATE與DELETE操作都會涉及相關聯的表,十分影響SQL的效能,甚至會造成死鎖。

【強制】(8)必須把欄位定義為NOT NULL並且提供預設值

解讀:a、NULL的列使索引/索引統計/值比較都更加複雜,對MySQL來說更難最佳化 b、NULL這種型別Msql內部需要進行特殊處理,增加資料庫處理記錄的複雜性;同等條件下,表中有較多空欄位的時候,資料庫的處理效能會降低很多 c、NULL值需要更多的儲存空,無論是表還是索引中每行中的NULL的列都需要額外的空間來標識

【強制】(9)禁用保留字,如DESC、RANGE、MARCH等,請參考Mysql官方保留字。

【強制】(10)如果儲存的字串長度幾乎相等,使用CHAR定長字串型別。

解讀:能夠減少空間碎片,節省儲存空間。更多SQL技巧可搜尋公號:SQL資料庫開發

【建議】(11)在一些場景下,考慮使用TIMESTAMP代替DATETIME。

解讀:a、這兩種型別的都能表達“yyyy-MM-dd HH:mm:ss”格式的時間,TIMESTAMP只需要佔用4個位元組的長度,可以儲存的範圍為(1970-2038)年,在各個時區,所展示的時間是不一樣的;b、而DATETIME型別佔用8個位元組,對時區不敏感,可以儲存的範圍為(1001-9999)年。

* 【建議】(12)當心自動生成的Schema,建議所有的Schema手動編寫。

解讀:對於一些資料庫客戶端不要太過信任

2

SQL規約

【建議】 (1) 為了充分利用快取,不允許使用自定義函式、儲存函式、使用者變數。

解讀:如果查詢中包含任何使用者

自定義函式、儲存函式、使用者變數、臨時表、Mysql庫中的系統表

,其查詢結果都不會被快取。比如函式NOW()或者CURRENT_DATE()會因為不同的查詢時間,返回不同的查詢結果。

【強制】(2)在查詢中指定所需的列,而不是直接使用“ *”返回所有的列

解讀:a)讀取不需要的列會增加CPU、IO、NET消耗 b)不能有效的利用覆蓋索引

【強制】(3)不允許使用屬性隱式轉換

解讀:假設我們在手機號列上添加了索引,然後執行下面的SQL會發生什麼?explain SELECT user_name FROM parent WHERE phone=13812345678;很明顯就是索引不生效,會全表掃描。

【建議】(4)在WHERE條件的屬性上使用函式或者表示式

解讀:Mysql無法自動解析這種表示式,無法使用到索引。

【強制】(5)禁止使用外來鍵與級聯,一切外來鍵概念必須在應用層解決。

解讀:外來鍵與級聯更新適用於單機低併發,

不適合分散式、高併發叢集;

級聯更新是強阻塞,存在資料庫更新風暴的風險;外來鍵影響資料庫的插入速度。

【建議】(6)應儘量避免在WHERE子句中使用or作為連線條件

解讀:根據情況可以選擇使用UNION ALL來代替OR

【強制】(7)不允許使用%開頭的模糊查詢

解讀:根據索引的最左字首原理,%開頭的模糊查詢無法使用索引,可以使用ES來做檢索。

3

索引規約

【建議】(1)避免在更新比較頻繁、區分度不高的列上單獨建立索引

解讀:區分度不高的列單獨建立索引的最佳化效果很小,但是較為頻繁的更新則會讓索引的維護成本更高

【強制】(2) JOIN的表不允許超過五個。需要JOIN的欄位,資料型別必須絕對一致; 多表關聯查詢時,保證被關聯的欄位需要有索引。

解讀:太多表的JOIN會讓Mysql的最佳化器更難權衡出一個“最佳”的執行計劃(可能性為表數量的階乘),同時要注意關聯欄位的型別、長度、字元編碼等等是否一致。

【強制】(3)在一個聯合索引中,若第一列索引區分度等於1,那麼則不需要建立聯合索引。

解讀:索引透過第一列就能夠完全定位的資料,所以聯合索引的後邊部分是不需要的。

【強制】(4)建立聯合索引時,必須將區分度更高的欄位放在左邊

解讀:區分度更高的列放在左邊,能夠在一開始就有效的過濾掉無用資料。提高索引的效率,相應我們在Mapper中編寫SQL的WHERE條件中有多個條件時,需要先看看當前表

是否有現成的聯合索引直接使用

,注意各個條件的順序儘量和索引的順序一致。

【建議】(5)利用覆蓋索引來進行查詢操作,避免回表

解讀:覆蓋查詢即是查詢只需要透過索引即可拿到所需DATA,而不再需要再次回表查詢,所以效率相對很高。我們在使用EXPLAIN的結果,extra列會出現:“using index”。這裡也要強調一下不要使用“SELECT * ”,否則幾乎不可能使用到覆蓋索引。

【建議】(6)在較長VARCHAR欄位,例如VARCHAR(100)上建立索引時,應指定索引長度,沒必要對全欄位建立索引,根據實際文字區分度決定索引長度即可。

解讀:索引的長度與區分度是一對矛盾體,一般對字串型別資料,若長度為20的索引,區分度會高達90%以上,則可以考慮建立長度例為20的索引,而非全欄位索引。例如可以使用SELECT COUNT(DISTINCT LEFT(lesson_code, 20)) / COUNT(*) FROM lesson;來確定lesson_code欄位字元長度為20時文字區分度。

【建議】(7)如果有ORDER BY的場景,請注意利用索引的有序性。ORDER BY最後的欄位是聯合索引的一部分,並且放在索引組合順序的最後,避免出現file_sort的情況,影響查詢效能。

解讀:1、假設有查詢條件為

WHERE a=? and b=? ORDER BY c

; 存在索引:a_b_c,則此時可以利用索引排序。2、反例:在查詢條件中包含了範圍查詢,那麼索引有序性無法利用,如:WHERE a>10 ORDER BY b; 索引a_b無法排序。

【建議】(8)在where中索引的列不能某個表示式的一部分,也不能是函式的引數。

解讀:即是某列上已經添加了索引,但是若此列成為表示式的一部分、或者是函式的引數,Mysql無法將此列單獨解析出來,索引也不會生效。

【建議】 (9)我們在where條件中使用範圍查詢時,索引最多用於一個範圍條件,超過一個則後邊的不走索引。

解讀:Mysql能夠使用多個範圍條件裡邊的最左邊的第一個範圍查詢,但是後邊的範圍查詢則無法使用。

【建議】 (10)在多個表進行外連線時,表之間的關聯欄位型別必須完全一致

解讀:當兩個表進行Join時,欄位型別若沒有完全一致,則加索引也不會生效,這裡的完全一致包括但不限於欄位型別、欄位長度、字符集、collection等等