MySQL索引效能分析

TandK發表於2021-01-21

為什麼要做效能分析

你有沒有這樣的情況。

面對一個你沒怎麼寫過的、複雜的業務,你構思了很久,終於開始敲下了第一段程式碼。

寫的過程迷迷糊糊,有的時候還能把自己搞暈了。

但你還是終於把它寫完了。

但是點選一執行,完了,有bug。

怎麼辦?

debug的方式有很多,控制檯列印是一種。

通過控制檯列印的資訊,我們能根據反饋去修改程式碼,直到程式碼能正常執行為止。

其實建索引也是一樣的。

上篇帖子《淺談sql索引》,說過索引的難點在於針對一個具體的表去做出最合適的索引。

因為這不只要看你表裡有什麼內容,更多要看你的業務,你的業務會經常根據哪些關鍵詞查詢。

程式碼我們可以天天寫,索引不能天天建吧。

所以多數情況下,因為不熟悉,我們一開始建立的索引往往都不是最好的,唯有根據反饋去調整索引,才能做出一個最合適這個表的索引。

今天要分享的就是怎麼去看懂這個反饋,即怎麼去做效能分析。

怎麼做效能分析

使用EXPLAIN關鍵字!

使用EXPLAIN關鍵字可以知道MySQL是如何處理你的SQL語句的,分析你的查詢語句或是表結構的效能瓶頸。

但EXPLAIN並不能直接反饋我們建的索引的好壞。

用法是這樣的,我們建好了索引,拿著業務中最常用的幾句SQL語句來EXPLAIN一下,如果反饋的效果好,那麼建立的索引就是最適合這個表的,反之則需要改進。

要不改索引,要不改SQL。

EXPLAIN玩法

語句

explain 要檢視的sql語句(橫表)或explain 要檢視的sql語句\G(豎表)

一個具體的例子

各個欄位解釋

從上圖中我們可以看到,explain出來的資訊有以下欄位:

id select_type table type possible_keys key key_len ref rows Extra

你仔細看看,這麼長是不是有點像工資條?

其中各個欄位代表的意思是這樣的:

  • id: 檢視錶的讀取順序。

    我們們上圖中的例子只查詢了一個表,但若是多表聯合查詢,則有:
    
          id相同的話,執行順序由上至下;
    
          id不同的話,id值越大優先順序越高;
    
  • select_type: 顯示區別聯合查詢、子查詢、普通查詢等。

    以下為其可能的值,以及對應所代表的資訊:
    
          SIMPLE -- 簡單的select查詢,不包含子查詢或union;
    
          PRIMARY -- 查詢中若包含任何複雜的子部分,最外層查詢則被標記為PRIMARY;
    
          SUBQUERY -- 在select或where包含的子查詢;
    
          DERIVED -- 在from列表包含的子查詢被標記為DERIVED(衍生),MySQL會遞迴執行這些子查詢,把結果放在臨時表裡;
    
          UNION -- 若第二個select出現在union後,則被標記為union;若union包含在from子句的子查詢中,外層select將被標記為:DERIVED;
    
          UNION RESULT -- 從union表獲取結果的select;
    
  • table: 顯示錶名。

  • type: 顯示查詢用了何種型別。

    以下為其可能的值,以及對應所代表的資訊:
    
          system -- 表只有一行記錄(等於系統表),這是const型別的特例,平時不會出現,可以忽略不記。
    
          const -- 表示通過索引一次就找到了,const用於比較primary key(主鍵)或者unique(唯一)索引。因為只匹配一行資料,所以很快,如果將主鍵置於where列表中,mysql就能將該查詢轉換到一個常量。
    
          eq_ref -- 唯一性掃描索引,對於每個索引鍵,表中**剛好只**有一條記錄與之匹配。常用於主鍵或唯一索引掃描。
    
          ref -- 非唯一性掃描索引,返回匹配某個單獨值的所有行。上面`eq_ref`的多值情況。如where age=1,age=1的資料剛好只有一行,便顯示eq_ref,age=1的資料有多行,便顯示ref。
    
          range -- 只檢索給定範圍的行,使用一個索引來選擇行。如果type列的值為`range`,key列便會顯示使用了哪個索引。一般就是在where語句中出現了between、<、>、in等的查詢。這種範圍掃描查詢比全表掃描要好。
    
          index -- Full Index Scan(掃描全部索引),index與ALL區別為index型別只遍歷索引樹。都是讀全表,但是index是從索引中讀取,all是從硬碟中讀,而且索引檔案通常比資料檔案小。
    
          all -- Full Table Scan(掃描全表),遍歷全表來找到匹配的行,即索引完全沒用上。
    
    從最好到最差依次是:system>const>eq_ref>ref>range>index>ALL
    
    細節:
    
          1. type 是 ALL,當資料到達百萬以上一定要優化。
    
          2. 一般來說,如果要優化得保證查詢至少達到range級別,最好達到ref。
    
  • possible_keys和key

    possible_key: 顯示可能應用在這張表中的索引,一個或多個。理論上可能被使用的索引,但不一定被查詢實際使用。
    
    key: 實際使用的索引,如果為null,則沒有使用索引。
    
    這兩列有四種情況:
          1. possible_key有值,key有值:正常,有的時候前者有多個值但後者只有一個也正常。
          2. possible_key有值,key無值:**索引失效了,出現問題了**。
          3. possible_key無值,key有值:條件查詢(如where)沒有用到索引或沒有條件查詢,但查詢的列(select後面的欄位)剛好順序、數量和索引一致。
          4. possible_key無值,key無值:正常,就是你沒建索引。
    
  • key_len: 表示索引中使用的位元組數。

    可通過該列計算【查詢中使用的索引的長度】,在查詢結果一樣的情況下,該值越小越好。
    
    key_len顯示的值為索引欄位的最大可能長度,而非實際使用長度,即通過表定義計算而得,不是通過表內檢索而得。
    
    假設你建立複合索引(col1,col2),如果【通過col1條件查詢】和【通過col1和col2條件查詢】的結果一樣,那麼前者比較好,因為只需要用一個欄位,key_len的值會比較小,上面也說過是通過表定義的長度來決定key_len的值。
    
  • ref: 顯示key列中索引參照的值。

    有兩種可能的值,以及對應所代表的資訊:
    
          庫名.表名.欄位名 -- 表示索引參照的值是哪個庫的哪個表的哪個欄位;
    
          const -- 表示索引參照的值是常量,一般是where id=1這樣才會出現;
    
  • rows: 根據表統計資訊以及索引選用情況,大致估算出找到要查詢的記錄需要讀取的行數。

  • Extra: 十分重要的額外資訊。

    以下為其可能的值,以及對應所代表的資訊:
    
          Using filesort -- 說明mysql完全或部分沒有按照你所建的索引排序,比較需要優化了。MySQL無法利用索引完成的排序操作稱為“檔案排序”;
    
          Using temporary-- 使用了臨時表儲存中間結果,mysql對查詢結果排序時使用臨時表。這也比較需要優化,因為臨時表的建立和刪除都是比較費效能的,常見於order by和group by;
    
          Using index -- 表示相應的select操作中使用了覆蓋索引,避免了訪問表的資料行,效率不錯。如果同時出現Using where,表明索引被用來執行索引鍵值的查詢,如果沒有出現,表明索引被用來讀取資料而非執行查詢動作;
    
          Using where -- 使用了where過濾;
    
          Using join buffer -- 使用了連線快取,如果總是出現這個欄位,可以去配置檔案中適當調大這個值;
    
          Impossible where -- where子句的值總是false,不能用來獲取任何元組;
    
          Select tables optimized away  -- 在沒有group by子句的情況下,基於索引優化MIN/MAX操作或者對於MyISAM儲存引擎優化count(*)操作,不必等到執行階段再進行計算,查詢執行計劃生成的階段即完成優化;
    
          distinct -- 優化distinct操作,在找到第一組匹配的元組後馬上停止找相同值的動作。
    
    注意:
    
          1. 使用group by等排序時,如果有用到索引,最好嚴格按照索引的順序來,比如,存在複合索引(col1,col2),排序時如果跳過col1,直接使用col2排序,會導致出現Using filesort、Using temporary等比較嚴重的問題。
    
          2. 儘量使用覆蓋索引,select後面的列名完全與建立的索引順序、數量一致。這樣可以直接使用索引讀取資料,避免讀取表的資料行。
    

案例

最後來看一個簡單的案例,我會先放題目,再放思路,最後放答案。

題目

要求是寫出SQL的執行順序。

思路

  1. 首先看id列,id越大優先順序越高,索引從id為4的那一行開始看,這一行的table為t2,即查詢的是t2,所以最先查詢的反而是最後的部分select name,id from t2

  2. id為3的這一行,table是t1,所以查的是select id,name from t1 where other_column='';這一行後面的Extra列的Using where同樣佐證了這點。

  3. id為2的這一行,table是t3,所以查的是select id from t3;另外,key列是primary說明用到了主鍵作為索引,Extra列的Using Index表示用到了覆蓋索引(即索引用在了select後面)。

  4. id為1的這一行,table是,意思是根據【id為3的那一行的查詢結果】來查詢(對應的你也可以看到id為3的那一行的select_type列為Derived),所以查的是select d1.name,(select id from t3)d2 from (select id,name from t1 where other_column = '') d1

  5. id為NULL這一行,table是<union1,4>,意思是根據【id為1和4的那兩行的查詢結果】來實現union查詢。

答案

1.select name,id from t2
2.select id,name from t1 where other_column=''
3.select id from t3
4.select d1.name,(3.result)d2 from (2.result) d1
5.(4.result) union (1.result)

最後還是建議結合前面每個欄位的解釋,有自己的一個思考過程是最好的。

最後

今天說的是如何看explain的結果。

就好像我們們控制檯列印資訊是為了看程式碼執行的情況一樣,看完了,如果有bug,還要動手改的。

而我們今天只是說如何看而已,下一步,就是如何改了。

相關文章