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