分析SQL執行計劃,需要關注哪些重要信息
下面是一次 explain 返回的一條 SQL 語句的執行計劃的內容:
+----+-------------+-------+------------+-------+---------------+----------+---------+------+------+----------+--------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------+---------------+----------+---------+------+------+----------+--------------------------+
| 1 | SIMPLE | t2 | NULL | index | NULL | idx_abc | 198 | NULL | 5 | 20.00 | Using where; Using index |
+----+-------------+-------+------------+-------+---------------+----------+---------+------+------+----------+--------------------------+
一個執行計劃中,共有 12 個字段,每個字段都十分重要。簡單介紹這 12 個字段:
- id:執行計劃中每個操作的獨特標識符。對于一條查詢語句,每個操作都有其唯一的 id。然而,在多表連接時,一次解釋中的多個記錄可能具有相同的 id。
- select_type:操作的種類。常見種類包括 SIMPLE、PRIMARY、SUBQUERY、UNION 等。不同種類的操作會影響查詢的執行效率。
- table:當前操作所涉及的表。
- partitions:當前操作所涉及的分區。
- type:表示查詢時所使用的索引類型,包括 ALL、index、range、ref、eq_ref、const 等。
- possible_keys:表示可能被查詢優化器選擇使用的索引。
- key:表示查詢優化器選擇使用的索引。
- key_len:表示索引的長度。索引的長度越短,查詢時的效率越高。
- ref:用來表示哪些列或常量被用來與 key 列中命名的索引進行比較。
- rows:表示此操作需要掃描的行數,即掃描表中多少行才能得到結果。
- filtered:表示此操作過濾掉的行數占掃描行數的百分比。該值越大,表示查詢結果越準確。
- Extra:表示其他額外的信息,包括 Using index、Using filesort、Using temporary 等。
假如我們有如下一張表(MySQL Innodb 5.7):
CREATE TABLE `t2` (
`id` INT(11),
`a` varchar(64) NOT NULL,
`b` varchar(64) NOT NULL,
`c` varchar(64) NOT NULL,
`d` varchar(64) NOT NULL,
`f` varchar(64) DEFAULT NULL,
PRIMARY KEY(id),
UNIQUE KEY `f` (`f`),
KEY `idx_abc` (`a`,`b`,`c`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
首先,我們來分析幾個重要字段的不同取值及其區別:
對于type字段,不同取值對查詢性能有顯著影響:
- system:表示系統表,數據量較小,通常不需要進行磁盤 IO。
- const:使用常數索引,MySQL 在查詢時只會使用常數值進行匹配。比如:
explain select * from t2 where f='Paidaxing';
- 此時使用了唯一性索引進行唯一查詢。
- eq_ref:唯一索引掃描,只會掃描索引樹中的一個匹配行。比如:
explain select * from t1 join t2 on t1.id = t2.id where t1.f = 'P';
- 當連接操作中使用了唯一索引或主鍵索引,并且連接條件是基于這些索引的等值條件時,MySQL 通常會選擇 eq_ref 連接類型,以提高查詢性能。
- ref:非唯一索引掃描,只會掃描索引樹中的一部分來查找匹配的行。比如:
explain select * from t2 where a = 'Paidaxing';
- 此時使用了非唯一索引進行查詢。
- range:范圍掃描,只會掃描索引樹中的一個范圍來查找匹配的行。比如:
explain select * from t2 where a > 'a' and a < 'c';
- 此時使用了索引進行性范圍查詢。
- index:全索引掃描,會遍歷索引樹來查找匹配的行。比如:
explain select c from t2 where b = 'P';
- 這里的 index 表示做了索引樹掃描,效率并不高,不符合最左前綴匹配的查詢。
- ALL:全表掃描,將遍歷全表來找到匹配的行。比如:
explain select * from t2 where d = "ni";
- 此時使用了非索引字段進行查詢。
需要注意的是,以上類型由快到慢排列為:system > const > eq_ref > ref > range > index > ALL。
接下來我們來探討兩個常被忽略但十分重要的字段:
possible_keys 和 key 字段:
- possible_keys(可能的索引):這一字段表示查詢語句中可能可以利用的索引,但并不一定實際使用這些索引。possible_keys 列出了所有可能用于查詢的索引,包括聯合索引的組合。
- key(使用的索引):相對應地,key 字段表示實際被查詢所使用的索引。如果在查詢中使用了索引,則該字段將顯示使用的索引名稱。它是實際用于查詢的索引。
接著說一個很重要!的字段,但是經常被忽略的字段 extra,這個字段描述了 MySQL 在執行查詢時所做的一些附加操作。下面是 Extra 可能的取值及其含義:
extra 字段:
- Using where(使用 where):這表示 MySQL 在檢索行后會再次進行條件過濾,使用 WHERE 子句進行進一步的篩選。這可能出現在列未被索引覆蓋,或者 where 篩選條件涉及非索引的前導列或非索引列。
explain select * from t2 where d = "ni"; # 非索引字段查詢
explain select d from t2 where b = "ni"; # 未索引覆蓋,用聯合索引的非前導列查詢
- Using index(使用索引):MySQL 使用了覆蓋索引來優化查詢,只需掃描索引而無需回到數據表中檢索行。
explain select b,c from t2 where a = "ni"; # 索引覆蓋
- Using index condition(使用索引條件):表示查詢在索引上執行了部分條件過濾,通常與索引下推有關。
explain select d from t2 where a = "ni" and b like "s%"; # 使用到索引下推。
- Using where; Using index(使用 where;使用索引):查詢的列被索引覆蓋,且 where 篩選條件是索引列之一,但不是索引的前導列,或者 where 篩選條件是索引列前導列的一個范圍。
explain select a from t2 where b = "ni"; # 索引覆蓋,但是不符合最左前綴
explain select b from t2 where a in ('a','d','sd'); # 索引覆蓋,但是前導列是個范圍
- Using join buffer(使用連接緩存):MySQL 使用了連接緩存。
explain select * from t1 join t2 on t1.id = t2.id where a = 's';
- Using temporary(使用臨時表):MySQL 創建了臨時表來存儲查詢結果,通常在排序或分組時發生。
explain select count(*),b from t2 group by b;
- Using filesort(使用文件排序):MySQL 將使用文件排序而不是索引排序,通常發生在無法使用索引進行排序時。
explain select count(*),b from t2 group by b;
- Using index for group-by(使用索引進行分組):MySQL 在分組操作中使用了索引。通常發生在分組操作涉及到索引中的所有列時。
- Using filesort for group-by(使用文件排序進行分組):MySQL 在分組操作中使用了文件排序。這通常發生在無法使用索引進行分組操作時。
- Range checked for each record(為每條記錄檢查范圍):表示 MySQL 在使用索引范圍查找時,需要對每一條記錄進行檢查。
- Using index for order by(使用索引進行排序):MySQL 在排序操作中使用了索引。通常發生在排序涉及到索引中的所有列時。
- Using filesort for order by(使用文件排序進行排序):MySQL 在排序操作中使用了文件排序。這通常發生在無法使用索引進行排序時。
- Using index for group-by; Using index for order by(在分組和排序中使用索引):表示 MySQL 在分組和排序操作中都使用了索引。
課外補充
如何判斷一條 SQL 走沒有索引
首先看 key 字段有沒有值,有值表示用到了索引樹,但是具體是怎么用的,還得看 type 和 extra。
簡單說以下幾個情況:
情況一:
explain select b from t2 where a in ('a','d','sd');
+----+-------+---------------+----------+--------------------------+
| id | type | possible_keys | key | Extra |
+----+-------+---------------+----------+--------------------------+
| 1 | index | NULL | idx_abc | Using where; Using index |
+----+-------+---------------+----------+--------------------------+
type = index,key = idx_abc,extra = 使用 where;使用 index。這表明查詢利用了 idx_abc 的聯合索引,但未嚴格遵守最左前綴匹配,或者雖然遵守了最左前綴,但在 a 字段上進行了范圍查詢。因此,實際上仍需掃描索引樹,效率并不理想。
情況二:
explain select * from t2 where a = 'Paidaxing';
+----+-------+---------------+----------+--------------------------+
| id | type | possible_keys | key | Extra |
+----+-------+---------------+----------+--------------------------+
| 1 | ref | idx_abc | idx_abc | NULL |
+----+-------+---------------+----------+--------------------------+
表示用到了索引進行查詢,并且用到的是 idx_abc 這個非唯一索引。
情況三:
explain select * from t2 where f = 'f';
+----+-------+---------------+----------+--------------------------+
| id | type | possible_keys | key | Extra |
+----+-------+---------------+----------+--------------------------+
| 1 | const | f | f | NULL |
+----+-------+---------------+----------+--------------------------+
表示用到了索引進行查詢,并且用到的是 f 這個唯一索引。
情況四:
explain select b,c from t2 where a = 'Paidaxing';
+----+-------+---------------+----------+--------------------------+
| id | type | possible_keys | key | Extra |
+----+-------+---------------+----------+--------------------------+
| 1 | ref | idx_abc | idx_abc | Using index |
+----+-------+---------------+----------+--------------------------+
表示用到了索引進行查詢,并且用到了 idx_abc 這個索引,而且查詢用到了覆蓋索引,不需要回表。
情況五:
explain select b,c from t2 where d = 'Paidaxing';
+----+-------+---------------+----------+--------------------------+
| id | type | possible_keys | key | Extra |
+----+-------+---------------+----------+--------------------------+
| 1 | ALL | NULL | NULL | Using where |
+----+-------+---------------+----------+--------------------------+
表示沒有用到索引。