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!!!