Mysql高效能優化規範建議
資料庫命令規範
-
所有資料庫物件名稱必須使用小寫字母並用下劃線分割
-
所有資料庫物件名稱禁止使用mysql保留關鍵字(如果表名中包含關鍵字查詢時,需要將其用單引號括起來)
-
資料庫物件的命名要能做到見名識意,並且最後不要超過32個字元
-
臨時庫表必須以tmp_為字首並以日期為字尾,備份表必須以bak_為字首並以日期(時間戳)為字尾
-
所有儲存相同資料的列名和列型別必須一致(一般作為關聯列,如果查詢時關聯列型別不一致會自動進行資料型別隱式轉換,會造成列上的索引失效,導致查詢效率降低)
資料庫基本設計規範
1. 所有表必須使用Innodb儲存引擎
2. 資料庫和表的字符集統一使用UTF8
3. 所有表和欄位都需要添加註釋
使用comment從句新增表和列的備註 從一開始就進行資料字典的維護
4. 儘量控制單表資料量的大小,建議控制在500萬以內
500萬並不是Mysql資料庫的限制,過大會造成修改表結構,備份,恢復都會有很大的問題
可以用歷史資料歸檔(應用於日誌資料),分庫分表(應用於業務資料)等手段來控制資料量大小
5. 謹慎使用Mysql分割槽表
分割槽表在物理上表現為多個檔案,在邏輯上表現為一個表 謹慎選擇分割槽鍵,跨分割槽查詢效率可能更低 建議採用物理分表的方式管理大資料
6. 儘量做到冷熱資料分離,減小表的寬度
Mysql限制每個表最多儲存4096列,並且每一行資料的大小不能超過65535位元組 減少磁碟IO,保證熱資料的記憶體快取命中率(表越寬,把表裝載進記憶體緩衝池時所佔用的記憶體也就越大, 也會消耗更多的IO) 更有效的利用快取,避免讀入無用的冷資料 經常一起使用的列放到一個表中(避免更多的關聯操作)
7. 禁止在表中建立預留欄位
預留欄位的命名很難做到見名識義 預留欄位無法確認儲存的資料型別,所以無法選擇合適的型別 對預留欄位型別的修改,會對錶進行鎖定
8. 禁止在資料庫中儲存圖片,檔案等大的二進位制資料
通常檔案很大,會短時間內造成資料量快速增長,資料庫進行資料庫讀取時,通常會進行大量的隨機IO操作,檔案很大時,IO操作很耗時 通常儲存於檔案伺服器,資料庫只儲存檔案地址資訊
9. 禁止在線上做資料庫壓力測試
10. 禁止從開發環境,測試環境直接連線生成環境資料庫
資料庫欄位設計規範
1. 優先選擇符合儲存需要的最小的資料型別
- 將字串轉換成數字型別儲存,如:將IP地址轉換成整形資料
mysql提供了兩個方法來處理ip地址
inet_aton 把ip轉為無符號整型(4-8位)
inet_ntoa 把整型的ip轉為地址
插入資料前,先用inet_aton把ip地址轉為整型,可以節省空間
顯示資料時,使用inet_ntoa把整型的ip地址轉為地址顯示即可。
- 對於非負型的資料(如自增ID、整型IP)來說,要優先使用無符號整型來儲存
因為:無符號相對於有符號可以多出一倍的儲存空間
SIGNED INT -2147483648~2147483647
UNSIGNED INT 0~4294967295
VARCHAR(N)中的N代表的是字元數,而不是位元組數
使用UTF8儲存255個漢字 Varchar(255)=765個位元組
過大的長度會消耗更多的記憶體
2. 避免使用TEXT、BLOB資料型別,最常見的TEXT型別可以儲存64k的資料
- 建議把BLOB或是TEXT列分離到單獨的擴充套件表中
Mysql記憶體臨時表不支援TEXT、BLOB這樣的大資料型別,如果查詢中包含這樣的資料,在排序等操作時,就不能使用記憶體臨時表,必須使用磁碟臨時表進行
而且對於這種資料,Mysql還是要進行二次查詢,會使sql效能變得很差,但是不是說一定不能使用這樣的資料型別
如果一定要使用,建議把BLOB或是TEXT列分離到單獨的擴充套件表中,查詢時一定不要使用select * 而只需要取出必要的列,不需要TEXT列的資料時不要對該列進行查詢
- TEXT或BLOB型別只能使用字首索引
因為MySQL對索引欄位長度是有限制的,所以TEXT型別只能使用字首索引,並且TEXT列上是不能有預設值的
3. 避免使用ENUM型別
修改ENUM值需要使用ALTER語句
ENUM型別的ORDER BY操作效率低,需要額外操作
禁止使用數值作為ENUM的列舉值
4. 儘可能把所有列定義為NOT NULL
原因:
索引NULL列需要額外的空間來儲存,所以要佔用更多的空間
進行比較和計算時要對NULL值做特別的處理
5. 使用TIMESTAMP(4個位元組)或DATETIME型別(8個位元組)儲存時間
TIMESTAMP 儲存的時間範圍 1970-01-01 00:00:01 ~ 2038-01-19-03:14:07
TIMESTAMP 佔用4位元組和INT相同,但比INT可讀性高
超出TIMESTAMP取值範圍的使用DATETIME型別儲存
經常會有人用字串儲存日期型的資料(不正確的做法)
缺點1:無法用日期函式進行計算和比較
缺點2:用字串儲存日期要佔用更多的空間
6. 同財務相關的金額類資料必須使用decimal型別
- 非精準浮點:float,double
- 精準浮點:decimal
Decimal型別為精準浮點數,在計算時不會丟失精度
佔用空間由定義的寬度決定,每4個位元組可以儲存9位數字,並且小數點要佔用一個位元組
可用於儲存比bigint更大的整型資料
索引設計規範
1. 限制每張表上的索引數量,建議單張表索引不超過5個
2. 禁止給表中的每一列都建立單獨的索引
3. 每個Innodb表必須有個主鍵
Innodb是一種索引組織表:資料的儲存的邏輯順序和索引的順序是相同的 每個表都可以有多個索引,但是表的儲存順序只能有一種 Innodb是按照主鍵索引的順序來組織表的 不要使用更新頻繁的列作為主鍵,不適用多列主鍵(相當於聯合索引) 不要使用UUID,MD5,HASH,字串列作為主鍵(無法保證資料的順序增長) 主鍵建議使用自增ID值
常見索引列建議
-
出現在SELECT、UPDATE、DELETE語句的WHERE從句中的列
-
包含在ORDER BY、GROUP BY、DISTINCT中的欄位
並不要將符合1和2中的欄位的列都建立一個索引, 通常將1、2中的欄位建立聯合索引效果更好
-
多表join的關聯列
如何選擇索引列的順序
- 區分度最高的放在聯合索引的最左側(區分度=列中不同值的數量/列的總行數)
- 儘量把欄位長度小的列放在聯合索引的最左側(因為欄位長度越小,一頁能儲存的資料量越大,IO效能也就越好)
- 使用最頻繁的列放到聯合索引的左側(這樣可以比較少的建立一些索引)
避免建立冗餘索引和重複索引(增加了查詢優化器生成執行計劃的時間)
重複索引示例:primary key(id)、index(id)、unique index(id) 冗餘索引示例:index(a,b,c)、index(a,b)、index(a)
對於頻繁的查詢優先考慮使用覆蓋索引
覆蓋索引:就是包含了所有查詢欄位(where,select,ordery by,group by包含的欄位)的索引
覆蓋索引的好處:
- 避免Innodb表進行索引的二次查詢
Innodb是以聚集索引的順序來儲存的,對於Innodb來說,二級索引在葉子節點中所儲存的是行的主鍵資訊,
如果是用二級索引查詢資料的話,在查詢到相應的鍵值後,還要通過主鍵進行二次查詢才能獲取我們真實所需要的資料
而在覆蓋索引中,二級索引的鍵值中可以獲取所有的資料,避免了對主鍵的二次查詢 ,減少了IO操作,提升了查詢效率
- 可以把隨機IO變成順序IO加快查詢效率
由於覆蓋索引是按鍵值的順序儲存的,對於IO密集型的範圍查詢來說,對比隨機從磁碟讀取每一行的資料IO要少的多,
因此利用覆蓋索引在訪問時也可以把磁碟的隨機讀取的IO轉變成索引查詢的順序IO
索引SET規範
儘量避免使用外來鍵約束
不建議使用外來鍵約束(foreign key),但一定要在表與表之間的關聯鍵上建立索引
外來鍵可用於保證資料的參照完整性,但建議在業務端實現
外來鍵會影響父表和子表的寫操作從而降低效能
資料庫SQL開發規範
1. 建議使用預編譯語句進行資料庫操作
預編譯語句可以重複使用這些計劃,減少SQL編譯所需要的時間,還可以解決動態SQL所帶來的SQL注入的問題 只傳引數,比傳遞SQL語句更高效 相同語句可以一次解析,多次使用, 提高處理效率
2. 避免資料型別的隱式轉換
隱式轉換會導致索引失效 如: select name,phone from customer where id = '111';
3. 充分利用表上已經存在的索引
避免使用雙%號的查詢條件。
如 a like '%123%',(如果無前置%,只有後置%,是可以用到列上的索引的)
一個SQL只能利用到複合索引中的一列進行範圍查詢
如 有 a,b,c列的聯合索引,在查詢條件中有a列的範圍查詢,則在b,c列上的索引將不會被用到, 在定義聯合索引時,如果a列要用到範圍查詢的話,就要把a列放到聯合索引的右側
使用left join 或 not exists 來優化not in 操作
因為not in 也通常會使用索引失效
4. 資料庫設計時,應該要對以後擴充套件進行考慮
5. 程式連線不同的資料庫使用不同的賬號,進位制跨庫查詢
為資料庫遷移和分庫分表留出餘地 降低業務耦合度 避免許可權過大而產生的安全風險
6. 禁止使用SELECT * 必須使用SELECT <欄位列表> 查詢
原因: 消耗更多的CPU和IO以網路頻寬資源 無法使用覆蓋索引 可減少表結構變更帶來的影響
7. 禁止使用不含欄位列表的INSERT語句
如: insert into values ('a','b','c'); 應使用 insert into t(c1,c2,c3) values ('a','b','c');
8. 避免使用子查詢,可以把子查詢優化為join操作
9. 避免使用JOIN關聯太多的表
對於Mysql來說,是存在關聯快取的,快取的大小可以由join_buffer_size引數進行設定 在Mysql中,對於同一個SQL多關聯(join)一個表,就會多分配一個關聯快取, 如果在一個SQL中關聯的表越多, 所佔用的記憶體也就越大 如果程式中大量的使用了多表關聯的操作,同時join_buffer_size設定的也不合理的情況下,就容易造成伺服器記憶體溢位的情況, 就會影響到伺服器資料庫效能的穩定性 同時對於關聯操作來說,會產生臨時表操作,影響查詢效率 Mysql最多允許關聯61個表,建議不超過5個
10. 減少同資料庫的互動次數
資料庫更適合處理批量操作 合併多個相同的操作到一起,可以提高處理效率
11. 對應同一列進行or判斷時,使用in代替or
in 的值不要超過500個 in 操作可以更有效的利用索引,or大多數情況下很少能利用到索引
12. 禁止使用order by rand() 進行隨機排序
13. WHERE從句中禁止對列進行函式轉換和計算
對列進行函式轉換或計算時會導致無法使用索引 不推薦: where date(create_time)='20190101' 推薦: where create_time >= '20190101' and create_time < '20190102'
14. 在明顯不會有重複值時使用UNION ALL 而不是UNION
UNION 會把兩個結果集的所有資料放到臨時表中後再進行去重操作 UNION ALL 不會再對結果集進行去重操作
15. 拆分複雜的大SQL為多個小SQL
大SQL:邏輯上比較複雜,需要佔用大量CPU進行計算的SQL MySQL 一個SQL只能使用一個CPU進行計算 SQL拆分後可以通過並行執行來提高處理效率
資料庫操作行為規範
超100萬行的批量寫(UPDATE、DELETE、INSERT)操作,要分批多次進行操作
1. 大批量操作可能會造成嚴重的主從延遲
主從環境中,大批量操作可能會造成嚴重的主從延遲,大批量的寫操作一般都需要執行一定長的時間,
而只有當主庫上執行完成後,才會在其他從庫上執行,所以會造成主庫與從庫長時間的延遲情況
2. binlog日誌為row格式時會產生大量的日誌
大批量寫操作會產生大量日誌,特別是對於row格式二進位制資料而言,由於在row格式中會記錄每一行資料的修改,我們一次修改的資料越多,
產生的日誌量也就會越多,日誌的傳輸和恢復所需要的時間也就越長,這也是造成主從延遲的一個原因
3. 避免產生大事務操作
大批量修改資料,一定是在一個事務中進行的,這就會造成表中大批量資料進行鎖定,從而導致大量的阻塞,阻塞會對MySQL的效能產生非常大的影響
特別是長時間的阻塞會佔滿所有資料庫的可用連線,這會使生產環境中的其他應用無法連線到資料庫,因此一定要注意大批量寫操作要進行分批
對於大表使用pt-online-schema-change修改表結構
- 避免大表修改產生的主從延遲
- 避免在對錶欄位進行修改時進行鎖表
對大表資料結構的修改一定要謹慎,會造成嚴重的鎖表操作,尤其是生產環境,是不能容忍的
pt-online-schema-change它會首先建立一個與原表結構相同的新表,並且在新表上進行表結構的修改,然後再把原表中的資料複製到新表中,並在原表中增加一些觸發器
把原表中新增的資料也複製到新表中,在行所有資料複製完成之後,把新表命名成原表,並把原來的表刪除掉
把原來一個DDL操作,分解成多個小的批次進行
禁止為程式使用的賬號賦予super許可權
當達到最大連線數限制時,還執行1個有super許可權的使用者連線 super許可權只能留給DBA處理問題的賬號使用
對於程式連線資料庫賬號,遵循許可權最小原則
程式使用資料庫賬號只能在一個DB下使用,不準跨庫 程式使用的賬號原則上不準有drop許可權