一、簡介
explain命令用於分析單條SQL語句,是查看優化器如何決定執行查詢的主要方法。
要使用EXPLAIN,只需在查詢中的SELECT關鍵字之前增加EXPLAIN這個詞。MySQL會在查詢上設置一個標記。當執行查詢時,這個標記會使其返回關於在執行計劃中每一步的信息,而不是執行它。它會返回一行或多行信息,顯示出執行計劃中的每一部分和執行的次序。
如:
mysql> explain select id from a where id =3\G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: a
type: ref
possible_keys: id
key: id
key_len: 5
ref: const
rows: 1
Extra: Using where; Using index
1 row in set (0.00 sec)
注意:認爲增加EXPLAIN時MySQL不會執行查詢,這是一個常見的錯誤。事實上,如果查詢在FROM子句中包括子查詢,那麼MySQL實際上會執行子查詢,將其結果放在一個臨時表中,然後完成外層查詢優化。
這意味着如果語句包含開銷比較大的子查詢或使用臨時表算法的視圖,實際上會給服務器帶來大量工作。
二、EXPLAIN的列
1、id
一個編號,標誌SELECT所屬的行。如果在語句當中沒有子查詢或聯合,那麼只會有唯一的SELECT,於是每一行都將顯一個1。否則,內層的SELECT語句一般會順序編號,對應於其在原始語句中的位置。
MySQL將SELECT查詢分爲簡單和複雜類型,複雜類型可以分爲三大類:簡單子查詢、所謂的派生表(在FROM子句的子查詢)、UNION查詢。
例(我們只看返回多少行數據就行,其它列先不用理):
# 例1:簡單子查詢
mysql> explain select (select 1 from a limit 1) from b;
+----+-------------+-------+-------+---------------+---------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+-------+---------------+---------+---------+------+------+-------------+
| 1 | PRIMARY | b | index | NULL | age | 2 | NULL | 4 | Using index |
| 2 | SUBQUERY | a | index | NULL | PRIMARY | 4 | NULL | 4 | Using index |
+----+-------------+-------+-------+---------------+---------+---------+------+------+-------------+
2 rows in set (0.00 sec)
# 例2:FROM子句中的子查詢
mysql> explain select name from (select name from a) as aa;
+----+-------------+------------+------+---------------+------+---------+------+------+-------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+------------+------+---------------+------+---------+------+------+-------+
| 1 | PRIMARY | <derived2> | ALL | NULL | NULL | NULL | NULL | 4 | |
| 2 | DERIVED | a | ALL | NULL | NULL | NULL | NULL | 4 | |
+----+-------------+------------+------+---------------+------+---------+------+------+-------+
2 rows in set (0.00 sec)
# 例3:UNION查詢
mysql> explain select id from a union select id from b;
+----+--------------+------------+-------+---------------+---------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+--------------+------------+-------+---------------+---------+---------+------+------+-------------+
| 1 | PRIMARY | a | index | NULL | PRIMARY | 4 | NULL | 4 | Using index |
| 2 | UNION | b | index | NULL | PRIMARY | 4 | NULL | 4 | Using index |
| NULL | UNION RESULT | <union1,2> | ALL | NULL | NULL | NULL | NULL | NULL | |
+----+--------------+------------+-------+---------------+---------+---------+------+------+-------------+
3 rows in set (0.00 sec)
注意UNION結果輸出中的額外行。UNION結果總是放在一個匿名的臨時表中,之後MySQL將結果取到臨時表外。臨時表並不在原SQL中出現,因此它的id列是NULL。
2、select_type
顯示對應和行是簡單還是複雜SELECT(如果是後者,那麼一三種複雜類型中的哪一種)。
SIMPLE值意味着查詢不包括子查詢和UNION。如果查詢中有任何複雜的子部分,則最外層標記爲PRIMARY,其它標記如下:
1)SUBQUERY
包含在SELECT列表中的子查詢中的SELECT(換句話說,不在FROM子句中)標記爲SUBQUERY。
2)DERIVED
包含在FROM子句的子查詢中的SELECT,MySQL會遞歸執行並將結果放到一個臨時表(服務器內部稱其“派生表”)。
3)UNION
UNION中的第二個和隨後的SELECT被標記爲UNION。
4)UNION RESULT
用來從UNION的匿名臨時表檢索結果的SELECT被標記爲UNION RESULT。
3、table
這一列顯示了對應正在訪問哪個表。通常情況下,它相當明瞭:它就是那個表,或者是該表的別名。
4、type
MySql用戶手冊上說這一列顯示了“關聯關類型”,但我們認爲更準確的說法是訪問類型——換言之就是MySQl決定如何查找表中的行。下面是最重要的訪問方法,依次從最差到最優。
- ALL
這就是我們所說的全表掃描。(當然也有例外,例如在查詢裏使用了LIMIT,或者在Extra列中顯示“Using distinct/not exists”) - index
這個跟全表掃描一樣,只是MySQL掃描表時按索引次序進行而不是行。它的主要優點是避免了排序;最大缺點是要承擔按索引次序讀取整個表的開銷。如果在Extra列中看到“Using index”,說明MySQL正在使用覆蓋索引,它只掃描索引的數據。而不是按索引次序的每一行。 - range
範圍掃描就是一個有限制的索引掃描,它開始於索引裏的某一點,返回匹配這個值域的行。這比全索引掃描好一些,因爲它不用遍歷全部索引。顯而易見的範圍掃描是帶有BETWEEN或在WHERE子句裏帶有>的查詢。 - ref
這是一種索引訪問(有時也叫做索引查找),它返回所有匹配是某個單值的行。然而它可能會找到多個符合條件的行,因此,它是查找和掃描的混合體。 - eq_ref
使用這種索引查找,MySQL知道最多隻返回一條符合條件的記錄。這種訪問方法可以在MySQL使用主鍵或者唯一性索引查找時看到。 - const, system
當MySQL能對查詢的某部分進行優化並將其轉化成一個常量時,它就會使用這些訪問類型。 - NULL
這種訪問方式意味着MySQL能在優化階段分解查詢語句,在執行階段甚至用不着再訪問表或者索引。例如,從一個索引列裏選取最小值可以通過單獨查找索引來完成,不需要在執行時訪問表。
5、possible_keys
這一列顯示了查詢可以使用哪些索引,這是基於查詢訪問的列和使用的比較操作符來判斷的。這個列表是在優化過程的早期創建的,因此有些羅列出來的索引可能對於後續優化過程是沒用的。
6、key
這一列顯示了MySQL決定採用哪個索引來優化對該表的訪問。如果該索引沒有出現在possible_keys列中,那麼MySQL選用它的另外的原因——例如,它可能選擇了一個覆蓋索引,哪怕沒有WHERE子句。
換句話說,possible_keys提示了哪一個索引能有助於高效地行查找。而key顯示的是優化採用哪一個索引可以最小化查詢成本。
7、key_len
該列顯示了MySQL在索引裏使用的字節數。如果MySQL正在使用的只是索引裏的某些列,那麼就可以用這個值來算出具體是哪些列。
8、ref
這一列顯示了之前表在key列記錄的索引中查找值所用的列或常量。
9、 rows
MySQL估計爲了找到所需的行而需要讀取的行數。
10、filtered
這一列是在MySQL 5.1裏新加進去的,在使用EXPLAIN EXTENDED時出現。它顯示的是針對表裏符合某個條件(WHERE子句或聯接條件)的記錄數的百分比所做的一個悲觀估算。如果你把row列和這個百分比相乘,就能看到MySQL估算它將和查詢計劃裏前一個表關聯的行數。
11、Extra
這一列包含的是不適合在其它列顯示的額外信息。常見的最重要的值如下:
- Using index
此值表示MySQL將使用覆蓋索引,以避免訪問表。 - Using where
這意味着MySQL服務器將在存儲引擎檢索行後再進行過濾。許多WHERE條件裏涉及索引中的列,當(並且如果)它讀取索引時,就能被存儲引擎檢驗,因此不是所有帶WHERE子句的查詢都會顯示“Using where”。 - Using temporary
這意味着MySQL 對查詢結果排序時會使用一個臨時表。 - Using filesort
這意味着MySQL會對結果使用一個外部索引排序,而不是按索引次序從表裏讀取行。 - Range checked for each record (index map: N)
這個值意味着沒有好用的索引,新的索引將在聯接的每一行上重新估算。N是顯示在possible_keys列中素引的位圖,並且是冗餘的。
參考
《高性能MySQL》 [美]Baron Scbwartz, Peter Zaitsev, Vadim Tkacbenko 著