資料庫原理三---MySQL資料庫優化
MySQL優化
MySQL優化分為以下幾個大類:
-
SQL調優
-
事務優化
-
表結構優化
-
使用快取和NoSQL資料庫方式儲存,如MongoDB/Memcached/Redis來緩解高併發下的資料庫查詢的壓力
-
減少資料庫操作次數,儘量使用資料庫訪問驅動的批處理方法
-
不常使用的資料遷移備份,避免每次都在海量資料中去檢索
SQL調優
資料庫調優在一般情況下都是SQL調優,那麼,應該如何進行SQL調優呢?
- 低效能SQL語句定位(找到有問題的SQL語句)
使用執行計劃explain
執行計劃,就是顯示資料庫引擎對於SQL語句的執行的詳細情況,其中包含了是否使用索引,使用什麼索引,使用的索引的相關資訊等。
執行計劃包含的資訊:- id
由一組數字組成。表示一個查詢中各個子查詢的執行順序;id相同執行順序由上至下,id不同,id值越大優先順序越高,越先被執行。id為null時表示一個結果集,不需要使用它查詢,常出現在包含union等查詢語句中。 - select_type
每個子查詢的查詢型別,一些常見的查詢型別。
- id
id | select_type | description |
---|---|---|
1 | SIMPLE | 不包含任何子查詢或union等查詢 |
2 | PRIMARY | 包含子查詢最外層查詢就顯示為 PRIMARY |
3 | SUBQUERY | 在select或 where字句中包含的查詢 |
4 | DERIVED | from字句中包含的查詢 |
5 | UNION | 出現在union後的查詢語句中 |
6 | UNION RESULT | 從UNION中獲取結果集 |
type:(非常重要,可以看到有沒有走索引) 訪問型別
all 掃描全表資料
index 遍歷索引
range 索引範圍查詢
index_subquery 在子查詢中使用 ref
unique_subquery 在子查詢中使用 eq_ref
ref_or_null 對Null進行索引的優化的 ref
fulltext 使用全文索引
ref 使用非唯一索引查詢資料
eq_ref 在join查詢中使用PRIMARY KEYorUNIQUE NOT NULL索引關聯。
possible_keys:可能使用的索引,注意不一定會使用。查詢涉及到的欄位上若存在索引,則該索引將被列出來。當該列為 NULL時就要考慮當前的SQL是否需要優化了。
key:顯示MySQL在查詢中實際使用的索引,若沒有使用索引,顯示為NULL。查詢中若使用了覆蓋索引,則該索引僅出現在key列表中
key_length:索引長度
ref:表示上述表的連線匹配條件,即哪些列或常量被用於查詢索引列上的值
rows:返回估算的結果集數目,並不是一個準確的值。
extra:執行情況的描述和說明,extra的資訊非常豐富,常見的有:
Using index 使用覆蓋索引
Using where 使用了用where子句來過濾結果集
Using filesort 使用檔案排序,使用非索引列進行排序時出現,非常消耗效能,儘量優化。
Using temporary 使用了臨時表 sql優化的目標可以參考阿里開發手冊
注: EXPALIN只能解釋SELECT操作,其他操作要重寫為SELECT後檢視執行計劃。
-
如果有告警資訊,檢視告警資訊 show warnings
-
檢視SQL涉及的表結構和索引資訊
-
根據執行計劃,思考可能的優化點
-
按照可能的優化點執行表結構變更、增加索引、SQL改寫等操作
-
檢視優化後的執行時間和執行計劃
-
如果優化效果不明顯,重複第四步操作
SQL改寫
-
使用連線(Join)來代替子查詢(Sub-Queries)
連線之所以更有效率一些,是因為MySQL不需要在記憶體中建立臨時表來完成這個邏輯上的需要兩個步驟的查詢工作。 -
UNION ALL能滿足業務需要不要使用UNION
UNION會自動壓縮多個結果集合中的重複結果,而UNION ALL則將所有的結果全部顯示出來,不管是不是重複。 -
WHERE子句儘量避免使用!=或<>操作符
在WHERE子句中使用!=或<>操作符,查詢條件不會使用索引,會進行全表查詢。即影響查詢效率。 -
WHERE子句使用OR優化
通常情況我們可以使用UNION ALL 或 UNION的方式替換OR會得到更好的效果。因為WHERE子句中使用了OR,將不會使用索引。 -
WHERE子句使用IN或NOT IN優化
IN和NOT IN也要慎用,否則可能會導致全表掃描
可用以下方案替換:
- BETWEEN AND 替換 IN
- EXISTS 替換 IN
- LEFT JOIN 替換 IN
-
WHERE子句使用IS NULL 或IS NOT NULL優化
在WHERE子句中使用IS NULL或IS NOT NULL判斷,索引將被放棄使用,會進行全表查詢。 -
一定不要使用SELECT * FROM
-
WHERE子句避免對欄位進行表示式操作
資料庫索引優化
-
索引覆蓋與回表查詢
如果要查詢的欄位都建立過索引,那麼索引會直接在索引表中查詢而不會訪問原始資料(否則只要有一個欄位沒有建立索引就會做全表掃描),這叫索引覆蓋。
因此我們需要儘可能的在select後只寫必要的查詢欄位,以增加索引覆蓋的機率。
(不要想著為每個欄位建立索引,因為優先使用索引的優勢就在於其體積小。)
回表查詢:先定位主鍵值,再根據主鍵值定位行記錄
hash索引任何時候都避免不了回表查詢資料,而B+樹在符合某些條件的時候可以只通過索引完成查詢。(具體留《MySQL架構體系》一文中補充,挖坑待填) -
建立索引的原則
- 較頻繁作為查詢條件的欄位才去建立索引
- 更新頻繁欄位不適合建立索引
- 不能有效區分資料的列不適合做索引列(如性別)
- 定義有外來鍵的資料列一定要建立索引
- 儘量擴充套件索引不要新建索引
事務優化
修改事務的隔離級別,具體檢視上文MySQL事務
表結構優化
- 資料庫表優化
-
設計規範化表,消除資料冗餘
資料庫三正規化
第一正規化:屬性(欄位)的原子性約束,要求屬性具有原子性,不可再分割
第二正規化:記錄的惟一性約束,要求記錄有惟一標識,每條記錄需要有一個屬性來做為實體的唯一標識
第三正規化:屬性(欄位)冗餘性的約束,即任何欄位不能由其他欄位派生出來,在通俗點就是:主鍵沒有直接關係的資料列必須消除(消除的辦法就是再建立一個表來存放他們,當然外來鍵除外)
滿足正規化的表,稱為規範化表
如果資料庫設計達到了完全的標準化,則把所有的表通過關鍵字連線在一起時,不會出現任何資料的複本(repetition)。
標準化的優點是明顯的,它避免了資料冗餘,自然就節省了空間,也對資料的一致性(consistency)提供了根本的保障,杜絕了資料不一致的現象,同時也提高了效率。 -
適當的冗餘,增加計算列
資料庫設計的實用原則是:在資料冗餘和處理速度之間找到合適的平衡點
滿足正規化的表一定是規範化的表,但不一定是最佳的設計。很多情況下會為了提高資料庫的執行效率,常常需要降低正規化標準:適當增加冗餘,達到以空間換時間的目的。
合理的冗餘可以分散資料量大的表的併發壓力,也可以加快特殊查詢的速度,冗餘欄位可以有效減少資料庫表的連線,提高效率。(例如合計、總量這種由其他欄位計算出來的列) -
欄位設計優化
欄位是資料庫最基本的單位,其設計對效能的影響是很大的。需要注意如下:
A、資料型別儘量用數字型,數字型的比較比字元型的快很多。
B、資料型別儘量小,這裡的儘量小是指在滿足可以預見的未來需求的前提下的。
C、儘量不要允許NULL,除非必要,可以用NOT NULL+DEFAULT代替。
D、少用TEXT和IMAGE,二進位制欄位的讀寫是比較慢的,而且,讀取的方法也不多,大部分情況下最好不用。
E、自增欄位要慎用,不利於資料遷移
-
讀寫分離(解決資料庫讀效能瓶頸)
將資料庫分為了主從庫,一個主庫用於寫資料,多個從庫完成讀資料的操作,主從庫之間通過某種機制進行資料的同步。(用來解決資料庫的讀效能瓶頸)
常見的資料庫瓶頸:資料容量的瓶頸
最好的解決辦法:資料庫水平切分 -
資料庫水平切分(解決資料容量瓶頸)
通過演算法,將資料庫進行分割的架構。一個水平切分叢集中的每個資料庫,通常被稱為一個"分片"。每一個分片中的資料沒有重合,所有分片中的資料並集組成全部資料。 -
資料庫垂直切分(降低單節點資料庫的負載)
根據業務來拆分資料庫,同一類業務的資料表拆分到一個獨立的資料庫,另一類的資料表拆分到其他資料庫。
垂直切分可以降低單節點資料庫的負載,不能解決縮表問題
通俗來講:水平拆分行,行資料拆分到不同的表中,垂直拆分列,表資料拆分到不同表中。
MySQL單表記錄超過2000萬,讀寫效能會下降的很快,因此說垂直切分並不能起到縮表的效果。