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

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