複習:Mysql 開發設計規範,這樣做沒有壞處

原文傳送門:https://blog.csdn.net/tian330726/article/details/88713086

一、基礎規範
二、命名規範
三、字段設計規範
四、索引設計規範
五、SQL查詢規範

一、基礎規範

  • 使用InnoDB 存儲引擎

沒有特殊要求(即Innodb無法滿足的功能如:列存儲,存儲空間數據等)的情況下,所有表必須使用Innodb存儲引擎(mysql5.5之前默認使用Myisam,5.6以後默認的爲Innodb)Innodb 支持事務,支持行級鎖,更好的恢復性,高併發下性能更好

  • 表字符集使用utf8mb4

默認使用 utf8mb4 字符集,數據庫排序規則使用 utf8mb4_general_ci,採用 utf8 編碼的 MySQL 無法保存佔位是 4 個字節的 Emoji 表情。爲了使後端的項目全面支持客戶端輸入的 Emoji 表情,升級編碼爲 utf8mb4 是最佳解決方案;

兼容性更好,統一字符集可以避免由於字符集轉換產生的亂碼,不同的字符集進行比較前需要進行轉換會造成索引失效

  • 所有表都需要添加註釋

使用comment從句添加表和列的備註 從一開始就進行數據字典的維護

  • 單表數據量建議控制在500萬以內

500萬並不是MySQL數據庫的限制,過大會造成修改表結構,備份,恢復都會有很大的問題,可以用歷史數據歸檔(應用於日誌數據),分庫分表(應用於業務數據)等手段來控制數據量大小

  • 不在數據庫中存儲圖、文件等大的二進制數據

通常文件很大,會短時間內造成數據量快速增長,數據庫進行數據庫讀取時,通常會進行大量的隨機IO操作,文件很大時,IO操作很耗時 通常存儲於文件服務器,數據庫只存儲文件地址信息

  • 儘量做到冷熱數據分離,減小表的寬度

MySQL限制每個表最多存儲4096列,並且每一行數據的大小不能超過65535字節 減少磁盤IO,保證熱數據的內存緩存命中率(表越寬,把表裝載進內存緩衝池時所佔用的內存也就越大,也會消耗更多的IO) 更有效的利用緩存,避免讀入無用的冷數據 經常一起使用的列放到一個表中(避免更多的關聯操作)

  • 禁止在線上做數據庫壓力測試
  • 禁止測試、開發環境直連數據庫

二、命名規範

1、庫名、表名、字段名必須使用小寫字母,並採用下劃線分割

  • MySQL有配置參數lower_case_table_names=1,即庫表名以小寫存儲,大小寫不敏感。如果是0,則庫表名以實際情況存儲,大小寫敏感;如果是2,以實際情況存儲,但以小寫比較
  • 如果大小寫混合使用,可能存在abc,Abc,ABC等多個表共存,容易導致混亂
  • 字段名顯示區分大小寫,但實際使用時不區分,即不可以建立兩個名字一樣但大小寫不一樣的字段
  • 爲了統一規範, 庫名、表名、字段名使用小寫字母,不允許 - 號

2、庫名、表名、字段名禁止超過32個字符,需見名知意,建議使用名詞不是動詞

庫名、表名、字段名支持最多64個字符,但爲了統一規範、易於辨識以及減少傳輸量,禁止超過32個字符

3、庫名、表名、字段名禁止使用MySQL保留字

當庫名、表名、字段名等屬性含有保留字時,SQL語句必須用反引號引用屬性名稱,這將使得SQL語句書寫、SHELL腳本中變量的轉義等變得非常複雜。

4、臨時庫、表名必須以tmp爲前綴,並以日期爲後綴

形如:tmp_user_account_20190313

5、備份庫、表必須以bak爲前綴,並以日期爲後綴

形如:bak_user_account_20190313

6、主鍵索引名爲 pk_ 字段名;唯一索引名爲 uk _ 字段名;普通索引名則爲 idx_ 字段名

pk_ 即 primary key;uk_ 即 unique key;idx_ 即 index 的簡稱

7、在不同的庫或表中,要保證所有存儲相同數據的列名和列類型必須一致

一般作爲關聯列,如果查詢時關聯列類型不一致會自動進行數據類型隱式轉換,會造成列上的索引失效,導致查詢效率降低

8、表的命名最好是加上“業務名稱_表的作用”

正例:user_task / force_project / trade_config

三、字段設計規範

1、優先選擇符合存儲需要的最小的數據類型

列的字段類型越大,建立索引佔據的空間就越大,導致一個頁中的索引越少,造成IO次數增加,影響性能

  • 業務中選擇性很少的狀態status、類型type等字段推薦使用tinytint或者smallint類型節省存儲空間
  • 能用int的就不用char或者varchar
  • 能用tinyint的就不用int
  • 使用 UNSIGNED 存儲非負數值
  • 使用tinyint來代替 enumboolean
  • 存儲 ip 最好用 int存儲而非 char(15)
通過MySQL函數inet_ntoa和inet_aton來進行轉化。IPv6地址目前沒有轉化函數,需要使用DECIMAL或兩個BIGINT來存儲
SELECT INET_ATON('209.207.224.40'); 3520061480
SELECT INET_NTOA(3520061480); 209.207.224.40
  • 表中的自增列(auto_increment屬性),推薦使用bigint類型

2、不推薦使用blobtext等類型

blobtext是爲了存儲極大的字符串而設計的數據類型,採用二進制與字符串方式存儲,該數據類型不能設置默認值、不便於排序、不便於建立索引, varchar 的性能會比 text 高很多,如果非要使用,建議將這種數據分離到單獨的拓展表中

3、禁止使用字符串來存儲日期型數據

  • 無法使用日期函數計算比較
  • 字符串存儲要佔更多的內存空間,datetime(8字節)和timestamp(本身是以int存儲,佔4字節,範圍:1970-01-01 00:00:01到2038-01-19 03:14:07)
  • TIMESTAMP 記錄經常變化的更新/創建/發佈/日誌時間等,並且是近來的時間,夠用,可免時區處理
  • DATETIME 記錄生日、紀念事件、超出 TIMESTAMP 的時間,記得時區處理

4、用 DECIMAL 代替 FLOAT 和 DOUBLE 存儲精確浮點數

Decimal類型爲精準浮點數,float 和 double 在存儲的時候,存在精度損失的問題,很可能在值的比較時,得到不正確的結果。如果存儲的數據範圍超過 decimal 的範圍,建議將數據拆成整數和小數分開存儲。

5、必須把字段定義爲NOT NULL並設默認值

  • NULL的列使用索引,索引統計,值都更加複雜,MySQL更難優化
  • NULL需要更多的存儲空間
  • NULL只能採用IS NULL或者IS NOT NULL,而在=/!=/in/not in時有大坑

6、使用varchar(20)存儲手機號,不要使用整數

  • 牽扯到國家代號,可能出現+/-/()等字符,例如+86
  • 手機號不會用來做數學運算
  • varchar可以模糊查詢,例如like ‘138%’

7、根據業務區分使用char/varchar

  • 字段長度固定,或者長度近似的業務場景,適合使用char,能夠減少碎片,查詢性能高
  • 字段長度相差較大,或者更新較少的業務場景,適合使用varchar,能夠減少空間

8、禁止在數據庫中存儲明文密碼,把密碼加密後存儲

9、儘量不使用外鍵

建議在應用層實現外鍵的邏輯, 外鍵與級聯更新不適合高併發場景,降低插入性能,大併發下容易產生死鎖

10、整形定義中不添加長度,比如使用INT,而不是INT[4]

值類型括號後面的數字只是表示寬度而跟存儲範圍沒有關係

11、核心表必須有行數據的創建時間和最後更新時間

核心表(如用戶表,金錢相關的表)必須有行數據的創建時間字段create_time和最後更新時間字段update_time,便於查問題

四、索引設計規範

索引其實就是一種數據結構,(哈希表、樹等等)不同類型的索引有着不同的數據結構和功能。

MySQL的查詢速度依賴良好的索引設計,因此索引對於高性能至關重要。合理的索引會加快查詢速度,不合理的索引會降低速度

0、索引的作用

  • 加速查詢速度
  • 維護數據的約束性(完整性、一致性)

對於加速查詢,使用索引不一定是最好的選擇。小表就直接全表掃描,中到大表就建索引,超大表就分區分表。其實主要就要索引帶來的好處和維護索引的成本之間的權衡。

1、單表的索引數建議不超過 5 個,單個索引中的字段數建議不超過 5 個

太多就起不到過濾作用了,索引也佔空間,管理起來也耗資源

2、對字符串使用前綴索引,前綴索引長度不超過8個字符

不要索引blob/text等字段,不要索引大型字段,這樣做會讓索引佔用太多的存儲空間

前綴索引就是對文本的前幾個字符建立索引,前綴索引能有效減小索引文件的大小,提高索引的速度。但是前綴索引也有它的壞處:MySQL 不能在 ORDER BY 或 GROUP BY 中使用前綴索引,也不能把它們用作覆蓋索引(Covering Index)

3、主鍵準則

  • 表必須有主鍵
  • 不使用更新頻繁的列
  • 儘量不選擇字符串列
  • 不使用UUID MD5 HASH
  • 默認使用非空的唯一鍵
  • 建議選擇自增或發號器

4、重要的SQL必須被索引,核心SQL優先考慮覆蓋索索引

  • UPDATE、DELETE 語句的 WHERE 條件列
  • ORDER BY、GROUP BY、DISTINCT 的字段
  • 多表 JOIN 的字段
  • 覆蓋索引可以避免Innodb表進行索引的二次查詢,把隨機IO變成順序IO,加快查詢效率

5、區分度最大的字段放在前面

  • 選擇篩選性更優的字段放在最前面,比如單號、userid等,type,status等篩選性一般不建議放在最前面
  • 索引根據左前綴原則,當建立一個聯合索引(a,b,c),則查詢條件裏面只有包含(a)或(a,b)或(a,b,c)的時候才能走索引,(a,c)作爲條件的時候只能使用到a列索引,所以這個時候要確定a的返回列一定不能太多,不然語句設計就不合理,(b,c)則不能走索引

6、業務上具有唯一特性的字段,即使是多個字段的組合,也必須建成唯一索引

不要以爲唯一索引影響了 insert 速度,這個速度損耗可以忽略,但提高查找速度是明
顯的; 另外,即使在應用層做了非常完善的校驗控制,只要沒有唯一索引,根據墨菲定律,必然有髒數據產生

7、InnoDB 和 MyISAM 存儲引擎表,索引類型必須爲BTREE

MEMORY表可以根據需要選擇 HASH 或者 BTREE 類型索引

00、MYSQL 中索引的限制

  • MYISAM 存儲引擎索引長度的總和不能超過 1000 字節
  • BLOB 和 TEXT 類型的列只能創建前綴索引
  • 使用不等於 (!= 或者 <>) 的時候, MYSQL 無法使用索引
  • 過濾字段使用函數運算 (如 abs (column)) 後, MYSQL無法使用索引
  • join語句中join條件字段類型不一致的時候MYSQL無法使用索引
  • 使用 LIKE 操作的時候如果條件以通配符開始 (如 ‘%abc…’)時, MYSQL無法使用索引。
  • 使用非等值查詢的時候, MYSQL 無法使用 Hash 索引

五、SQL查詢規範

1、按需索取,拒絕 select *

  • 無法索引覆蓋,回表操作,增加 io
  • 額外的內存負擔,大量冷數據灌入innodb_buffer_pool_size,降低查詢命中率
  • 額外的網絡傳輸開銷

2、能確定返回結果只有一條時,使用 limit 1

在保證數據不會有誤的前提下,能確定結果集數量時,多使用limit,儘快的返回結果。

3、涉及到複雜sql時,務必先參考已有索引設計,先explain

  • 簡單SQL拆分,不以代碼處理複雜爲由
  • 比如 OR 條件: f_phone=’10000’ or f_mobile=’10000’,兩個字段各自有索引,但只能用到其中一個。可以拆分成2個sql,或者union all
  • 先explain的好處是可以爲了利用索引,增加更多查詢限制條件

4、對應同一列進行 or 判斷時,使用 in 代替 or

in 的值不要超過 500 個, in 操作可以更有效的利用索引,or 大多數情況下很少能利用到索引

5、禁止使用 order by rand() 進行隨機排序

  • 會把表中所有符合條件的數據裝載到內存中,然後在內存中對所有數據根據隨機生成的值進行排序,並且可能會對每一行都生成一個隨機值,如果滿足條件的數據集非常大,就會消耗大量的 CPU 和 IO 及內存資源
  • 推薦在程序中獲取一個隨機值,然後從數據庫中獲取數據的方式

6、WHERE從句中禁止對列進行函數轉換和計算

對列進行函數轉換或計算時會導致無法使用索引

不推薦:where date(create_time)='20190101'
推薦:where create_time >= '20190101' and create_time < '20190102'

7、不要使用 count(列名)或 count(常量)來替代 count(*)

count(*)是 SQL92 定義的標準統計行數的語法,跟數據庫無關,跟 NULL 和非 NULL 無關

count(*)會統計值爲 NULL 的行,而 count(列名)不會統計此列爲 NULL 值的行

8、不得使用外鍵與級聯,一切外鍵概念必須在應用層解決

以學生和成績的關係爲例,學生表中的 student_id是主鍵,那麼成績表中的 student_id則爲外鍵。如果更新學生表中的 student_id,同時觸發成績表中的 student_id 更新, 即爲級聯更新。外鍵與級聯更新適用於單機低併發,不適合分佈式、高併發集羣; 級聯更新是強阻塞,存在數據庫更新風暴的風險; 外鍵影響數據庫的插入速度

9、in 操作能避免則避免

若實在避免不了,需要仔細評估 in 後邊的集合元素數量,控制在 1000 個之內

10、超過三個表禁止 join

需要 join 的字段,數據類型必須絕對一致;多表關聯查詢時,保證被關聯的字段需要有索引;即使雙表 join 也要注意表索引、 SQL 性能

11、SELECT語句不要使用UNION,推薦使用UNION ALL

UNION子句個數限制在5個以內。因爲union all不需要去重,節省數據庫資源,提高性能

12、建議使用合理的分頁方式以提高分頁效率

不推薦 SELECT * FROM table ORDER BY TIME DESC LIMIT 10000,10;
原因:會導致大量的io,因爲MySQL使用的是提前讀取策略
推薦:SELECT * FROM table WHERE TIME < last_TIME ORDER BY TIME DESC LIMIT 10.
SELECT * FROM table inner JOIN (SELECT id FROM table ORDER BY TIME LIMIT 10000,10) as t USING(id)

MySQL分頁查詢的性能優化 — 詳細說明

13、減少與數據庫交互次數,儘量採用批量SQL語句

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章