MySQL查詢優化--Explain

Explain是我們平時使用最多的MySQL優化關鍵字了,瞭解它的使用是掌握MySQL優化的基礎。當在查詢語句前加上Explain關鍵字,MySQL會展示引擎優化後的sql執行計劃,除此之外,還可以在Explain後面加上Extended關鍵字,它可以提供額外的一些資訊,我們可以通過執行計劃來優化sql的執行效率。
先來看個例子,我們執行一條簡單的sql來看一下Explain的輸出。

MySQL給我們返回了一個表格,瞭解每列所代表的含義是我們優化SQL的前提。讓我們分別來介紹一下。
id
代表著查詢的序列號。這個序列號代表著語句執行的順序。id相同執行順序從上到下;id不同id值越大,優先順序越高,越先被執行。
select_type
在說它之前,先來了解一下MySQL的表連線演算法,下面會用到,MySQL對於表連線使用nested-loop方法,該演算法表示MySQL將會在第一個表中讀取一條資料,然後在第二個表、第三個表等尋找匹配的記錄。當所有的表都被處理,MySQL再從第一個表繼續讀取下一行,如此迴圈。類似於巢狀for迴圈。
foreach rowin t1 matching range { foreach rowin t2 matching reference key { foreach rowin t3 { if row satisfies join conditions, send to client } } }
說回來,該列代表著查詢型別,它的值有多個,這裡挑幾個重要的來講。
SIMPLE:簡單查詢,不包含子查詢與union操作
PEIMARY:主查詢,即上面我們說的nested-loop最外層的for迴圈
UNION:SQL中含有union查詢時nested-loop的內層迴圈
UNION RESULT:union的結果集
SUBQUERY:子查詢中的第一個查詢
DERIVED:查詢產生的派生表
table
這個比較直接,代表著查詢使用的表名
type
這一列比較重要,這一列的值代表著SQL的執行效率的好壞,我們把常見的結果值排個序,從好到壞依次是
system:表只有一行或者查詢的是系統表
const:最多有一行匹配,通常在主鍵精確匹配時type會為該型別,例如SELECT * FROM tbl_name WHERE primary_key =1
eq_ref:唯一性索引掃描。當連線使用索引的所有部分並且索引是PRIMARY KEY或UNIQUE NOT NULL索引時使用它,例如SELECT * FROM ref_table , other_table WHERE ref_table . key_column = other_table . column ;
ref:非唯一性索引掃描。ref可用於使用=或<=>運算子進行比較的索引列。例如SELECT * FROM ref_table WHERE key_column = expr
ref_or_null:類似於ref,但是MySQL對於為空值的列做了額外的搜尋,常見於解析子查詢,例如SELECT * FROM ref_table WHERE key_column = expr OR key_column IS NULL
range:使用索引檢索指定範圍內的資料
index:會掃描整個索引樹
ALL:全表掃描
一般來說我們要保證大表得查詢至少要達到range級別,最好達到ref,否則就可能會出現效能問題。
possible_keys
指出查詢可能使用的索引,注意並不是真正使用的索引
key
查詢真正使用的索引。0查詢中若使用了覆蓋索引(select 後要查詢的欄位剛好和建立的索引欄位完全相同),則該索引僅出現在key列表中 (意味著possible_keys為null)
key_len
查詢使用的索引的長度
ref
顯示索引的被使用列(聯合索引)
rows
MySQL預計會檢索的行數
filtered
按條件篩選行數的百分比
Extra
包含MySQL解析查詢的一些額外資訊,該列可能的值有太多,不一一介紹,只說幾個比較重要的。
Using filesort:order by關鍵字使用了檔案排序,即在無索引的列上進行排序,出現它意味著可能需要進行SQL優化
Using temporary :為滿足查詢需要建立臨時表,同上
Using index:表示查詢使用了覆蓋索引,避免訪問了表的資料行,效率還可以。
一些小tips
優化SQL時都需要檢視執行計劃,然後對SQL進行改寫,直到得到滿意的type。
這裡寫幾個基本的查詢時需要注意的地方,幫助大家少走彎路。
1.關聯查詢時關聯列長度與型別要相同,若一個為char(10)一個為char(15)則不能使用索引
2.如果order by和group by列名不同或者來自不同的表那麼將會產生臨時表
3.join語句,小表驅動大表,即小表在join前,大表在join後。
4.索引最佳左字首原則:若索引了多列,則查詢時從索引最左列開始是可以使用索引的。舉個例子,比如有index_a_b_c,abc三列的聯合索引,查詢時 條件= a,條件= ab,條件= abc均可使用索引,而條件=bc或ac則無法使用索引,總結一句話“帶頭大哥不能丟,中間兄弟不能斷”(這是BTree索引的實現上導致的,具體見我的另一篇文章 MySQL中的幾種索引介紹 )
5.不能在索引列上做函式計算,型別轉換等操作,會導致索引失效,例如 條件=9-1
6.儘量使用覆蓋索引,少使用select *
7.like以萬用字元“%”開頭將無法使用索引
8.關於exists與in:
select from a where exists(select from b where a.id=b.id )先執行外查詢
select from a where a.id in (select from b),先執行內查詢
由上面的nested-loop表連線演算法可以得到如下結論,a表比b表大用in,a比b小用exists。
9.not in與not exists使用not exists,因為後者會使用索引。
Join 查詢總結
接下來給大家一張福利局,SQL中所有的join情形都可以在這張圖裡體現,一圖在手,天下我有!

注:由於mysql不支援full join,所以第6條和7條查詢需要改寫
6改寫後: 1+union+2 (注:union自帶去重)
7改寫後: 4+union+5
最後
SQL的改寫其實是很難的一件事情(自己總被diss SQL效能差),能夠進行簡單的索引優化只是基礎,瞭解官方文件是熟練掌握SQL改寫的前提,所以呢有時間啃啃文件是極好的 MySQL5.7官方文件 。
希望大家都能在實踐中不斷進步,寫出不被DBA diss的SQL!!!