MySQL 的 explain 語句顯示了 MySQL 如何使用索引來處理 select 語句以及連接表,可以幫助我們分析如何選擇更好的索引和寫出更優性能的查詢語句
查詢示例
explain select * from (select * from `user` limit 10, 20) as t1
left join `user_profile` as t2 on t1.id = t2.user_id;
-
id(查詢標識符)這是 SELECT 查詢序列號,表明 sql 語句執行的順序,id 列數字越大越先執行,如果數字一樣大,那么就從上往下依次執行,id 列為 null 的就表是這是一個結果集,不需要使用它來進行查詢。例如:
explain select * from (select * from `user` limit 10, 10) as s;
id.png -
select_type(查詢類型)常見類型有:
- simple: 表示不需要 union 操作或者不包含子查詢的 select。有連接查詢時,外層的查詢為 simple,且只有一個
- primary: 一個需要 union 操作或者含有子查詢的 select,位于最外層的單位查詢的 select_type 即為primary,且只有一個
- union:union 連接的兩個 select,第一個查詢是 dervied 派生表,除了第一個表外,第二個以后的表的 select_type 都是 union。例如:
explain select * from `user` union (select * from `user` limit 10);
select_type-union.png - dependent union:與union一樣,出現在 union 或 union all語句中,但是這個查詢要受到外部查詢的影響。
- union result:包含 union 的結果集,在 union 和 union all 語句中,因為它不需要參與查詢,所以 id 字段為 null。例如:
explain select * from `user` union (select * from `user` limit 10);
select_type-union.png - subquery:除了 from 字句中包含的子查詢外,其它地方出現的子查詢都可能是 subquery
- dependent subquery:與 dependent union 類似,表示這個 subquery 的查詢要受到外部表查詢的影響
- derived:from 字句中出現的子查詢,也叫做派生表,其他數據庫中可能叫做內聯視圖或嵌套 select。例如:
explain select * from (select * from `user` limit 10, 10) as s;
id.png
table (查詢表名) 如果查詢使用了別名,那么這里顯示的是別名,如果不涉及對數據表的操作,那么這里顯示為 null,如果顯示為尖括號括起來的<derived N>就表示這個是臨時表,后邊的 N 就是執行計劃中的 id,表示結果來自于這個查詢產生。如果是尖括號括起來的<union M,N>,與<derived N>類似,也是一個臨時表,表示這個結果來自于 union 查詢的 id為 M,N 的結果集
-
type(連接類型)需要重點掌握。有多個參數,依次從好到差:system,const,eq_ref,ref,fulltext,ref_or_null,unique_subquery,index_subquery,range,index_merge,index,ALL. 除了 ALL 之外,其他的 type 都可以使用到索引,除了 index_merge 之外,其他的 type 只可以用到一個索引
- system:表中只有一行數據或者是空表,且只能用于 MyISAM 和 Memory表。如果是 InnoDB 引擎表,type 列在這個情況通常都是 all 或者 index。
- const:使用 primary key 或者 unique 索引,返回記錄一定是 1 行記錄的等值 where 條件時,通常 type 是 const。其他數據庫也叫做唯一索引掃描。例如:primary key,
explain select * from `user` where id = 1220;
type-const_1.pngexplain select * from `user` where username = 'fanxin';
type-const_2.png - eq_ref 對于 eq_ref 的解釋,MySQL 手冊是這樣說的:"對于每個來自于前面的表的行組合,從該表中讀取一行。這可能是最好的聯接類型,除了 const 類型。它用在一個索引的所有部分被聯接使用并且索引是 UNIQUE 或 PRIMARY KEY"。eq_ref 可以用于使用=比較帶索引的列。例如:primary key,
explain select * from `user` as t1, `user_profile` as t2 where t1.id = t2.user_id;
type-const_1.pngexplain select * from `user` where username = 'fanxin';
type-eq_ref.png - ref:不像 eq_ref 那樣要求連接順序,也沒有主鍵和唯一索引的要求,只要使用相等條件檢索時就可能出現,常見與輔助索引的等值查找?;蛘叨嗔兄麈I、唯一索引中,使用第一個列之外的列作為等值查找也會出現,總之,返回數據不唯一的等值查找就可能出現。
- ref_or_null:與 ref 方法類似,只是增加了 null 值的比較。實際用的不多。** 上面這五種情況都是很理想的索引使用情況 **
- fulltext:全文索引檢索,要注意,全文索引的優先級很高,若全文索引和普通索引同時存在時,MySQL 不管代價,優先選擇使用全文索引。
- unique_subquery:用于 where 中的 in 形式子查詢,子查詢返回不重復值唯一值。
- index_subquery:用于 in 形式子查詢使用到了輔助索引或者 in 常數列表,子查詢可能返回重復值,可以使用索引將子查詢去重。
- range:索引范圍掃描,常見于使用 >, <, is null, between, in, like 等運算符的查詢中。例如:
explain select * from `user` where id > 1220;
type-range.png - index_merge:表示查詢使用了兩個以上的索引,最后取交集或者并集,常見and ,or的條件使用了不同的索引,官方排序這個在ref_or_null之后,但是實際上由于要讀取所個索引,性能可能大部分時間都不如 range。
- index:索引全表掃描,把索引從頭到尾掃一遍,常見于使用索引列就可以處理不需要讀取數據文件的查詢、可以使用索引排序或者分組的查詢。
- all:這個就是全表掃描數據文件,然后再在server層進行過濾返回符合要求的記錄。
possible_keys(查詢可能使用到的索引)
key(查詢真正使用到的索引)select_type 為 index_merge 時,這里可能出現兩個以上的索引,其他的 select_type 這里只會出現一個。
-
key_len(用于處理查詢的索引長度)
- 如果是單列索引,那么整個索引長度都算進去
- 如果是多列索引,那么查詢不一定都能使用到所有的列,具體使用到了多少個列的索引,這里就會計算進去,沒有使用到的列,這里不會計算進去。留意下這個列的值,算一下你的多列索引總長度就知道有沒有使用到所有的列了。要注意,MySQL 的ICP 特性使用到的索引不會計入其中。另外,key_len 只計算where 條件用到的索引長度,而排序和分組就算用到了索引,也不會計算到 key_len 中。
ref 列顯示使用哪個列或常數與key一起從表中選擇行。如果是使用的常數等值查詢,這里會顯示 const,如果是連接查詢,被驅動表的執行計劃這里會顯示驅動表的關聯字段,如果是條件使用了表達式或者函數,或者條件列發生了內部隱式轉換,這里可能顯示為 func。
rows MySQL 執行查詢的行數,不是精確值。簡單且重要,數值越大越不好,說明沒有用好索引。
-
Extra 該列包含 MySQL 解決查詢的詳細信息。
- distinct:在 select 部分使用了 distinc 關鍵字
- no tables used:不帶 from 字句的查詢或者 From dual 查詢
- 使用not in()形式子查詢或not exists運算符的連接查詢,這種叫做反連接。即,一般連接查詢是先查詢內表,再查詢外表,反連接就是先查詢外表,再查詢內表。
- using filesort:排序時無法使用到索引時,就會出現這個。常見于 order by 和 group by 語句中。例如:
explain select id,username from user order by created_at;
extra-using-filesort.png - using index:查詢時不需要回表查詢,直接通過索引就可以獲取查詢的數據。
- using join buffer(block nested loop),using join buffer(batched key accss):5.6.x之后的版本優化關聯查詢的BNL,BKA特性。主要是減少內表的循環數量以及比較順序地掃描查詢。
- using sort_union,using_union,using intersect,using sort_intersection:using intersect:表示使用 and 的各個索引的條件時,該信息表示是從處理結果獲取交集 using union:表示使用 or 連接各個使用索引的條件時,該信息表示從處理結果獲取并集 using sort_union 和 using sort_intersection:與前面兩個對應的類似,只是他們是出現在用 and 和 or 查詢信息量大時,先查詢主鍵,然后進行排序合并后,才能讀取記錄并返回。
- 表示使用了臨時表存儲中間結果。臨時表可以是內存臨時表和磁盤臨時表,執行計劃中看不出來,需要查看status變量,used_tmp_table,used_tmp_disk_table才能看出來。
- using where:表示存儲引擎返回的記錄并不是所有的都滿足查詢條件,需要在 server 層進行過濾。查詢條件中分為限制條件和檢查條件,5.6 之前,存儲引擎只能根據限制條件掃描數據并返回,然后 server 層根據檢查條件進行過濾再返回真正符合查詢的數據。5.6.x 之后支持 ICP 特性,可以把檢查條件也下推到存儲引擎層,不符合檢查條件和限制條件的數據,直接不讀取,這樣就大大減少了存儲引擎掃描的記錄數量。extra 列顯示 using index condition。
- firstmatch(tb_name):5.6.x 開始引入的優化子查詢的新特性之一,常見于 where 字句含有 in() 類型的子查詢。如果內表的數據量比較大,就可能出現這個。
- loosescan(m..n):5.6.x 之后引入的優化子查詢的新特性之一,在 in() 類型的子查詢中,子查詢返回的可能有重復記錄時,就可能出現這個。
filtered 使用explain extended時會出現這個列,5.7之后的版本默認就有這個字段,不需要使用explain extended了。這個字段表示存儲引擎返回的數據在server層過濾后,剩下多少滿足查詢的記錄數量的比例,注意是百分比,不是具體記錄數。