執行計劃欄位概要說明

id
查詢語句中每出現一個 SELECT 關鍵字,MySQL 就會為它分配一個唯一的 id 值。也有例外,比如優化器對子查詢做了 semi-join 優化時,和關聯查詢一樣兩個查詢的 id 是一樣的:
mysql> explain select * from t1 where a in (select b from t2 where t2.b=100);
+----+-------------+-------+------------+------+---------------+------+---------+-------+------+----------+--------------------------------------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+-------+------+----------+--------------------------------------------------------------------+
| 1 | SIMPLE | t1 | NULL | ref | a | a | 5 | const | 1 | 100.00 | NULL |
| 1 | SIMPLE | t2 | NULL | ALL | NULL | NULL | NULL | NULL | 1 | 100.00 | Using where; FirstMatch(t1); Using join buffer (Block Nested Loop) |
+----+-------------+-------+------------+------+---------------+------+---------+-------+------+----------+--------------------------------------------------------------------+
另外一個比較特殊的是 id 為 NULL,比如:
mysql> explain select * from t1 union select * from t2;
+----+--------------+------------+------------+------+---------------+------+---------+------+------+----------+-----------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+--------------+------------+------------+------+---------------+------+---------+------+------+----------+-----------------+
| 1 | PRIMARY | t1 | NULL | ALL | NULL | NULL | NULL | NULL | 1000 | 100.00 | NULL |
| 2 | UNION | t2 | NULL | ALL | NULL | NULL | NULL | NULL | 1 | 100.00 | NULL |
| NULL | UNION RESULT | <union1,2> | NULL | ALL | NULL | NULL | NULL | NULL | NULL | NULL | Using temporary |
+----+--------------+------------+------------+------+---------------+------+---------+------+------+----------+-----------------+
這是因為 union 結果是要去重的,內部創建了一個 <union1,2> 名字的臨時表,把查詢 1 和查詢 2 的結果集都合併到這個臨時表中,利用唯一鍵進行去重,這種情況下查詢 id 就為 NULL。
select_type
表示查詢的類型,

1. SIMPLE
查詢語句中不包含 UNION 或者子查詢的查詢都算作是 SIMPLE 類型,比方說下邊這個單表查詢的 select_type 的值就是 SIMPLE:
mysql> explain select * from t1 where b=1 order by a;
+----+-------------+-------+------------+------+---------------+-------+---------+-------+------+----------+---------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+-------+---------+-------+------+----------+---------------------------------------+
| 1 | SIMPLE | t1 | NULL | ref | idx_b | idx_b | 5 | const | 1 | 100.00 | Using index condition; Using filesort |
+----+-------------+-------+------------+------+---------------+-------+---------+-------+------+----------+---------------------------------------+
關聯查詢也是 SIMPLE 類型:
mysql> explain select * from t1 join t2 on t1.a=t2.a;
+----+-------------+-------+------------+------+---------------+------+---------+-----------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+-----------+------+----------+-------------+
| 1 | SIMPLE | t2 | NULL | ALL | a | NULL | NULL | NULL | 1 | 100.00 | Using where |
| 1 | SIMPLE | t1 | NULL | ref | a | a | 5 | hucq.t2.a | 1 | 100.00 | NULL |
+----+-------------+-------+------------+------+---------------+------+---------+-----------+------+----------+-------------+
2. PRIMARY
對於包含 union 或者子查詢的大查詢來說,它是由幾個小查詢組成的,其中最左邊的那個查詢的 select_type 值就是 PRIMARY,比如:
mysql> explain select * from t1 union select * from t2;
+----+--------------+------------+------------+------+---------------+------+---------+------+------+----------+-----------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+--------------+------------+------------+------+---------------+------+---------+------+------+----------+-----------------+
| 1 | PRIMARY | t1 | NULL | ALL | NULL | NULL | NULL | NULL | 1000 | 100.00 | NULL |
| 2 | UNION | t2 | NULL | ALL | NULL | NULL | NULL | NULL | 1 | 100.00 | NULL |
| NULL | UNION RESULT | <union1,2> | NULL | ALL | NULL | NULL | NULL | NULL | NULL | NULL | Using temporary |
+----+--------------+------------+------------+------+---------------+------+---------+------+------+----------+-----------------+
3. UNION
位於 union 或者 union all 查詢中第二個及其以後的查詢被標記為 UNION,第一個被標記為 primary ,可以對比上一個例子的效果。
4. UNION RESULT
MySQL 選擇使用臨時表來完成 UNION 查詢的去重工作,針對該臨時表的查詢的 select_type 就是 UNION RESULT,跟上一個例子一樣。
5. SUBQUERY
當子查詢不能被優化成 semi-join,但可以用 materialization 來優化,顯示為 SUBQUERY,表示這是一個不相關子查詢,子查詢只需要執行一遍(因為結果被物化成臨時表了),舉例:
mysql> SELECT * FROM t1 WHERE t1.a IN (SELECT t2.b FROM t2 WHERE id < 10);
+----+-------------+-------+-------+---------+------+------+-------------+
| id | select_type | table | type | key | ref | rows | Extra |
+----+-------------+-------+-------+---------+------+------+-------------+
| 1 | PRIMARY | t1 | ALL | NULL | NULL | 100 | Using where |
| 2 | SUBQUERY | t2 | range | PRIMARY | NULL | 9 | Using where |
+----+-------------+-------+-------+---------+------+------+-------------+
6. DEPENDENT SUBQUERY
當子查詢不能被優化成 semi-join,並且是一個相關子查詢(或者非相關子查詢關閉 materialization 優化策略時),會被優化器轉化成 exists 相關子查詢來進行查詢,顯示為 DEPENDENT SUBQUERY,表示這是一個相關子查詢,會根據外查詢結果執行多次。舉例:
mysql> SELECT * FROM t1 WHERE t1.a IN (SELECT t2.b FROM t2 WHERE id < 10);
+----+--------------------+-------+-------+---------+------+----------+-------------+
| id | select_type | table | type | key | rows | filtered | Extra |
+----+--------------------+-------+-------+---------+------+----------+-------------+
| 1 | PRIMARY | t1 | ALL | NULL | 100 | 100.00 | Using where |
| 2 | DEPENDENT SUBQUERY | t2 | range | PRIMARY | 9 | 10.00 | Using where |
+----+--------------------+-------+-------+---------+------+----------+-------------+
7. MATERIALIZED
當子查詢被優化成 semi-join 執行,並且 semi-join 用的是 Materialize 策略,這個子查詢對應顯示就是 MATERIALIZED,然後用子查詢結果物化後的臨時表與另一張表進行關聯查詢。舉例:
mysql> SELECT * FROM t1 WHERE t1.a IN (SELECT t2.b FROM t2 WHERE id < 10);
+----+--------------+-------------+-------+---------+---------------+------+-------------+
| id | select_type | table | type | key | ref | rows | Extra |
+----+--------------+-------------+-------+---------+---------------+------+-------------+
| 1 | SIMPLE | <subquery2> | ALL | NULL | NULL | NULL | Using where |
| 1 | SIMPLE | t1 | ref | a | <subquery2>.b | 1 | NULL |
| 2 | MATERIALIZED | t2 | range | PRIMARY | NULL | 9 | Using where |
+----+--------------+-------------+-------+---------+---------------+------+-------------+
8. DERIVED
對於無法使用 derived_merge 優化的派生子查詢,優化器會採用物化的方式執行的包含派生表的子查詢,該派生表對應的子查詢的 select_type 就是 DERIVED,舉例:
mysql> explain select * from t1 join (select distinct a from t2) as derived_t2 on t1.a=derived_t2.a;
+----+-------------+------------+------------+-------+---------------+-------------+---------+----------------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+------------+------------+-------+---------------+-------------+---------+----------------+------+----------+-------------+
| 1 | PRIMARY | t1 | NULL | ALL | a | NULL | NULL | NULL | 100 | 100.00 | Using where |
| 1 | PRIMARY | <derived2> | NULL | ref | <auto_key0> | <auto_key0> | 5 | join_test.t1.a | 10 | 100.00 | Using index |
| 2 | DERIVED | t2 | NULL | index | a | a | 5 | NULL | 1000 | 100.00 | Using index |
+----+-------------+------------+------------+-------+---------------+-------------+---------+----------------+------+----------+-------------+
關於子查詢和派生表的優化策略,可具體查看對應的文章:MySQL 子查詢優化、SQL 優化:derived 派生表優化。
type
type 顯示的是訪問類型,是較為重要的一個指標,結果值從好到壞依次是:system > const > eq_ref > ref > fulltext > ref_or_null > index_merge > unique_subquery > index_subquery > range > index > ALL ,一般來說,得保證查詢至少達到 range 級別,最好能達到 ref。

1. system
當表中只有一條記錄並且該表使用的存儲引擎的統計數據是精確的,比如 MyISAM、Memory,那麼對該表的訪問方法就是 system:
mysql> explain select * from t_myisam;
+----+-------------+----------+------------+--------+---------------+------+---------+------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+----------+------------+--------+---------------+------+---------+------+------+----------+-------+
| 1 | SIMPLE | t_myisam | NULL | system | NULL | NULL | NULL | NULL | 1 | 100.00 | NULL |
+----+-------------+----------+------------+--------+---------------+------+---------+------+------+----------+-------+
InnoDB 表即使只有一行,也不是 system,而是 ALL:
mysql> explain select * from t5; +----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------+
| 1 | SIMPLE | t5 | NULL | ALL | NULL | NULL | NULL | NULL | 1 | 100.00 | NULL |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------+
2. const
根據主鍵或者唯一二級索引列與單個常數進行等值匹配時(不能有多個條件用 or 連接,這屬於範圍查詢),對單表的訪問方法就是 const,舉例:
mysql> explain select * from t1 where id=100;
+----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
| 1 | SIMPLE | t1 | NULL | const | PRIMARY | PRIMARY | 4 | const | 1 | 100.00 | NULL |
+----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
3. eq_ref
在連接查詢時,如果被驅動表是通過主鍵或者唯一二級索引列等值匹配的方式進行訪問的(如果該主鍵或者唯一二級索引是聯合索引的話,所有的索引列都必須進行等值比較),則對該被驅動表的訪問方法就是 eq_ref,舉例:
mysql> explain select * from t1 join t2 on t1.id=t2.id where t1.a<50;
+----+-------------+-------+------------+--------+---------------+---------+---------+------------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+--------+---------------+---------+---------+------------+------+----------+-------------+
| 1 | SIMPLE | t2 | NULL | ALL | PRIMARY | NULL | NULL | NULL | 1 | 100.00 | NULL |
| 1 | SIMPLE | t1 | NULL | eq_ref | PRIMARY,a | PRIMARY | 4 | hucq.t2.id | 1 | 5.00 | Using where |
+----+-------------+-------+------------+--------+---------------+---------+---------+------------+------+----------+-------------+
4. ref
當通過普通的二級索引列與常量進行等值匹配時,那麼對該表的訪問方法就是 ref,即使匹配到的值可能是多行,舉例:
mysql> explain select * from t11 where a=100;
+----+-------------+-------+------------+------+---------------+------+---------+-------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+-------+------+----------+-------+
| 1 | SIMPLE | t11 | NULL | ref | a | a | 5 | const | 500 | 100.00 | NULL |
+----+-------------+-------+------------+------+---------------+------+---------+-------+------+----------+-------+
5. ref_or_null
當對普通二級索引進行等值匹配查詢,該索引列的值也可以是 NULL 值時,那麼對該表的訪問方法就可能是 ref_or_null,舉例:
mysql> explain select * from t11 where a=100 or a is null;
+----+-------------+-------+------------+-------------+---------------+------+---------+-------+------+----------+-----------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------------+---------------+------+---------+-------+------+----------+-----------------------+
| 1 | SIMPLE | t11 | NULL | ref_or_null | a | a | 5 | const | 501 | 100.00 | Using index condition |
+----+-------------+-------+------------+-------------+---------------+------+---------+-------+------+----------+-----------------------+
6. index_merge
一般情況下對於某個表的查詢只能使用到一個索引,在某些場景下可以使用 Intersection、Union、Sort-Union 這三種索引合併的方式來執行查詢,此時就顯示為 index_merge,舉例:
mysql> explain select * from t1 where a<50 or b=50;
+----+-------------+-------+------------+-------------+---------------+---------+---------+------+------+----------+----------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------------+---------------+---------+---------+------+------+----------+----------------------------------------+
| 1 | SIMPLE | t1 | NULL | index_merge | a,idx_b | a,idx_b | 5,5 | NULL | 50 | 100.00 | Using sort_union(a,idx_b); Using where |
+----+-------------+-------+------------+-------------+---------------+---------+---------+------+------+----------+----------------------------------------+
7. unique_subquery
對一些包含 in 子查詢的查詢語句中,如果優化器無法使用 semi-join 或物化進行優化,最終將子查詢轉換為 EXISTS 子查詢,而且子查詢可以使用到主鍵或者唯一鍵進行等值匹配的話,那麼該子查詢執行計劃的type列的值就是 unique_subquery。舉例:
mysql> explain select * from t1 where a in(select id from t2 where t1.a=t2.a) or b=100;
+----+--------------------+-------+------------+-----------------+---------------+---------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+--------------------+-------+------------+-----------------+---------------+---------+---------+------+------+----------+-------------+
| 1 | PRIMARY | t1 | NULL | ALL | idx_b | NULL | NULL | NULL | 1000 | 100.00 | Using where |
| 2 | DEPENDENT SUBQUERY | t2 | NULL | unique_subquery | PRIMARY,a | PRIMARY | 4 | func | 1 | 100.00 | Using where |
+----+--------------------+-------+------------+-----------------+---------------+---------+---------+------+------+----------+-------------+
8. range
如果使用索引獲取某些範圍區間的記錄,那麼就可能使用到 range 訪問方法。舉例:
mysql> explain select * from t1 where a<50 and a>20;
+----+-------------+-------+------------+-------+---------------+------+---------+------+------+----------+-----------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------+---------------+------+---------+------+------+----------+-----------------------+
| 1 | SIMPLE | t1 | NULL | range | a | a | 5 | NULL | 29 | 100.00 | Using index condition |
+----+-------------+-------+------------+-------+---------------+------+---------+------+------+----------+-----------------------+
mysql> explain select * from t1 where a in(1,2,3);
+----+-------------+-------+------------+-------+---------------+------+---------+------+------+----------+-----------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------+---------------+------+---------+------+------+----------+-----------------------+
| 1 | SIMPLE | t1 | NULL | range | a | a | 5 | NULL | 3 | 100.00 | Using index condition |
+----+-------------+-------+------------+-------+---------------+------+---------+------+------+----------+-----------------------+
9. index
需要掃描全部的索引記錄時,該表的訪問方法就是 index,成本很高。舉例:
mysql> EXPLAIN SELECT key_part1 FROM s1 WHERE key_part3 = 'a';
+----+-------------+-------+------------+-------+---------------+--------------+---------+------+------+----------+--------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------+---------------+--------------+---------+------+------+----------+--------------------------+
| 1 | SIMPLE | s1 | NULL | index | NULL | idx_key_part | 909 | NULL | 9688 | 10.00 | Using where; Using index |
+----+-------------+-------+------------+-------+---------------+--------------+---------+------+------+----------+--------------------------+
possible_keys 和 key
在 EXPLAIN 語句輸出的執行計劃中,possible_keys 列表示在某個查詢語句中,對某個表執行單表查詢時可能用到的索引有哪些,key 列表示實際用到的索引有哪些。
有些時候使用到的索引未必在 passible_keys 中,上面 type 為 index 的示例中,passible_keys 顯示為 NULL,但實際 key 顯示是使用到索引的。possible_keys 列中的值並不是越多越好,可能使用的索引越多,查詢優化器計算查詢成本時就得花費更長時間,所以如果可以的話,盡量刪除那些用不到的索引。
key_len
key_len 列顯示 MySQL 決定使用的鍵長度。如果鍵是 NULL,則長度為 NULL。使用的索引的長度。在不損失精確性的情況下,長度越短越好 。
ref
當使用索引列等值匹配的條件去執行查詢時,也就是在訪問方法是 const、eq_ref、ref、ref_or_null、unique_subquery、index_subquery 其中之一時,ref 列展示的就是與索引列作等值匹配的對象是啥。如果不是等值查詢,則顯示為 NULL。
比如單表查詢時肯定是個常數 const:
mysql> explain select * from t1 where a=100;
+----+-------------+-------+------------+------+---------------+------+---------+-------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+-------+------+----------+-------+
| 1 | SIMPLE | t1 | NULL | ref | a | a | 5 | const | 1 | 100.00 | NULL |
+----+-------------+-------+------------+------+---------------+------+---------+-------+------+----------+-------+
而關聯查詢中則是驅動表的關聯欄位 t2.a:
mysql> explain select * from t1 join t2 on t1.a=t2.a where t1.a<50;
+----+-------------+-------+------------+-------+---------------+------+---------+-----------+------+----------+-----------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------+---------------+------+---------+-----------+------+----------+-----------------------+
| 1 | SIMPLE | t2 | NULL | range | a | a | 5 | NULL | 1 | 100.00 | Using index condition |
| 1 | SIMPLE | t1 | NULL | ref | a | a | 5 | hucq.t2.a | 1 | 100.00 | NULL |
+----+-------------+-------+------------+-------+---------------+------+---------+-----------+------+----------+-----------------------+
rows
如果查詢優化器決定使用全表掃描的方式對某個表執行查詢時,執行計劃的 rows 列就代表預計需要掃描的行數;如果使用索引來執行查詢時,執行計劃的 rows 列就代表預計掃描的索引記錄行數。
這有可能是個精確值,也可能是個估算值,計算方法有 index dive 和基於統計索引信息的估算。
filtered
對於單表查詢來說:
- 如果是全表掃描,filtered 值代表滿足 where 條件的行數占表總行數的百分比;
- 如果是使用索引來執行查詢,filtered 值代表從索引上取得數據後,滿足其他過濾條件的數據行數的佔比。
mysql> explain select * from t1 where a<100 and b > 100;
+----+-------------+-------+------------+-------+---------------+------+---------+------+------+----------+------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------+---------------+------+---------+------+------+----------+------------------------------------+
| 1 | SIMPLE | t1 | NULL | range | a | a | 5 | NULL | 99 | 33.33 | Using index condition; Using where |
+----+-------------+-------+------------+-------+---------------+------+---------+------+------+----------+------------------------------------+
上面示例中,從 a 索引上取得 99 行數據,優化器估算認為這 99 行數據中有 33.33% 滿足 b > 100 這個條件。對於單表查詢來說,這個意義不大。
對於關聯查詢來說,驅動表的 rows*(filtered/100) 代表優化器認為的扇出,對於關聯查詢的成本估算有很大的影響。舉例:
mysql> EXPLAIN SELECT * FROM s1 INNER JOIN s2 ON s1.key1 = s2.key1 WHERE s1.common_field = 'a';
+----+-------------+-------+------------+------+---------------+----------+---------+-------------------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+----------+---------+-------------------+------+----------+-------------+
| 1 | SIMPLE | s1 | NULL | ALL | idx_key1 | NULL | NULL | NULL | 9688 | 10.00 | Using where |
| 1 | SIMPLE | s2 | NULL | ref | idx_key1 | idx_key1 | 303 | xiaohaizi.s1.key1 | 1 | 100.00 | NULL |
+----+-------------+-------+------------+------+---------------+----------+---------+-------------------+------+----------+-------------+
從執行計劃中可以看出來,查詢優化器打算把 s1 當作驅動表,s2 當作被驅動表。我們可以看到驅動表 s1 表的執行計劃的 rows 列為 9688, filtered 列為 10.00,這意味著驅動表 s1 的扇出值就是 9688 × 10.00% = 968.8,這說明還要對被驅動表執行大約 968 次查詢。
Extra
Extra 是 EXPLAIN 輸出中另外一個很重要的列,該列顯示 MySQL 在查詢過程中的一些詳細信息。

由於對其中幾個狀態有疑惑,所以這部分內容寫到另一篇文章中單獨討論。
原創文章,作者:投稿專員,如若轉載,請註明出處:https://www.506064.com/zh-tw/n/223016.html