explain顯示了mysql如何使用索引來處理select語句以及鏈接表。能夠幫助選擇更好的索引和寫出更優化的查詢語句。mysql
使用方法,在select語句前加上explain就能夠了:sql
如:express
explain select surname,first_name form a,b where a.id=b.id
EXPLAIN列的解釋:函數
table:顯示這一行的數據是關於哪張表的性能
type:這是重要的列,顯示鏈接使用了何種類型。從最好到最差的鏈接類型爲const、eq_reg、ref、range、index和ALL優化
type顯示的是訪問類型,是較爲重要的一個指標,結果值從好到壞依次是:spa
system > const > eq_ref > ref > fulltext > ref_or_null > index_merge > unique_subquery > index_subquery > range > index > ALL
通常來講,得保證查詢至少達到range級別,最好能達到ref。設計
possible_keys:顯示可能應用在這張表中的索引。若是爲空,沒有可能的索引。能夠爲相關的域從WHERE語句中選擇一個合適的語句指針
key: 實際使用的索引。若是爲NULL,則沒有使用索引。不多的狀況下,MYSQL會選擇優化不足的索引。這種狀況下,能夠在SELECT語句中使用USE INDEX(indexname)來強制使用一個索引或者用IGNORE INDEX(indexname)來強制MYSQL忽略索引code
key_len:使用的索引的長度。在不損失精確性的狀況下,長度越短越好
ref:顯示索引的哪一列被使用了,若是可能的話,是一個常數
rows:MYSQL認爲必須檢查的用來返回請求數據的行數
Extra:關於MYSQL如何解析查詢的額外信息。將在表4.3中討論,這裏能夠看到的壞的例子是Using temporary和Using filesort,意思MYSQL根本不能使用索引,結果是檢索會很慢
extra列返回的描述的意義
Distinct:一旦MYSQL找到了與行相聯合匹配的行,就再也不搜索了
Not exists: MYSQL優化了LEFT JOIN,一旦它找到了匹配LEFT JOIN標準的行,就再也不搜索了
Range checked for each Record(index map:#):沒有找到理想的索引,所以對於從前面表中來的每個行組合,MYSQL檢查使用哪一個索引,並用它來從表中返回行。這是使用索引的最慢的鏈接之一
Using filesort: 看到這個的時候,查詢就須要優化了。MYSQL須要進行額外的步驟來發現如何對返回的行排序。它根據鏈接類型以及存儲排序鍵值和匹配條件的所有行的行指針來排序所有行
Using index: 列數據是從僅僅使用了索引中的信息而沒有讀取實際的行動的表返回的,這發生在對錶的所有的請求列都是同一個索引的部分的時候
Using temporary 看到這個的時候,查詢須要優化了。這裏,MYSQL須要建立一個臨時表來存儲結果,這一般發生在對不一樣的列集進行ORDER BY上,而不是GROUP BY上
Where used 使用了WHERE從句來限制哪些行將與下一張表匹配或者是返回給用戶。若是不想返回表中的所有行,而且鏈接類型ALL或index,這就會發生,或者是查詢有問題不一樣鏈接類型的解釋(按照效率高低的順序排序)
system 表只有一行:system表。這是const鏈接類型的特殊狀況
const:表中的一個記錄的最大值可以匹配這個查詢(索引能夠是主鍵或唯一索引)。由於只有一行,這個值實際就是常數,由於MYSQL先讀這個值而後把它當作常數來對待
eq_ref:在鏈接中,MYSQL在查詢時,從前面的表中,對每個記錄的聯合都從表中讀取一個記錄,它在查詢使用了索引爲主鍵或唯一鍵的所有時使用
ref:這個鏈接類型只有在查詢使用了不是唯一或主鍵的鍵或者是這些類型的部分(好比,利用最左邊前綴)時發生。對於以前的表的每個行聯合,所有記錄都將從表中讀出。這個類型嚴重依賴於根據索引匹配的記錄多少—越少越好
range:這個鏈接類型使用索引返回一個範圍中的行,好比使用>或<查找東西時發生的狀況
index: 這個鏈接類型對前面的表中的每個記錄聯合進行徹底掃描(比ALL更好,由於索引通常小於表數據)
ALL:這個鏈接類型對於前面的每個記錄聯合進行徹底掃描,這通常比較糟糕,應該儘可能避免
先看一個例子:
mysql> explain select * from t_order; +----+-------------+---------+------+---------------+------+---------+------+--------+-------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+---------+------+---------------+------+---------+------+--------+-------+ | 1 | SIMPLE | t_order | ALL | NULL | NULL | NULL | NULL | 100453 | | +----+-------------+---------+------+---------------+------+---------+------+--------+-------+ 1 row in set (0.03 sec)
加上extended後以後:
mysql> explain extended select * from t_order; +----+-------------+---------+------+---------------+------+---------+------+--------+----------+-------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+---------+------+---------------+------+---------+------+--------+----------+-------+ | 1 | SIMPLE | t_order | ALL | NULL | NULL | NULL | NULL | 100453 | 100.00 | | +----+-------------+---------+------+---------------+------+---------+------+--------+----------+-------+ 1 row in set, 1 warning (0.00 sec)
有必要解釋一下這個長長的表格裏每一列的含義:
id | SELECT識別符。這是SELECT的查詢序列號 |
select_type | SELECT類型,能夠爲如下任何一種:
|
table | 輸出的行所引用的表 |
type | 聯接類型。下面給出各類聯接類型,按照從最佳類型到最壞類型進行排序:
|
possible_keys | 指出MySQL能使用哪一個索引在該表中找到行 |
key | 顯示MySQL實際決定使用的鍵(索引)。若是沒有選擇索引,鍵是NULL。 |
key_len | 顯示MySQL決定使用的鍵長度。若是鍵是NULL,則長度爲NULL。 |
ref | 顯示使用哪一個列或常數與key一塊兒從表中選擇行。 |
rows | 顯示MySQL認爲它執行查詢時必須檢查的行數。多行之間的數據相乘能夠估算要處理的行數。 |
filtered | 顯示了經過條件過濾出的行數的百分比估計值。 |
Extra | 該列包含MySQL解決查詢的詳細信息
|
一.select_type的說明
1.UNION:
當經過union來鏈接多個查詢結果時,第二個以後的select其select_type爲UNION。
mysql> explain select * from t_order where order_id=100 union select * from t_order where order_id=200; +----+--------------+------------+-------+---------------+---------+---------+-------+------+-------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+--------------+------------+-------+---------------+---------+---------+-------+------+-------+ | 1 | PRIMARY | t_order | const | PRIMARY | PRIMARY | 4 | const | 1 | | | 2 | UNION | t_order | const | PRIMARY | PRIMARY | 4 | const | 1 | | | NULL | UNION RESULT | <union1,2> | ALL | NULL | NULL | NULL | NULL | NULL | | +----+--------------+------------+-------+---------------+---------+---------+-------+------+-------+ 3 rows in set (0.34 sec)
2.DEPENDENT UNION與DEPENDENT SUBQUERY:
當union做爲子查詢時,其中第二個union的select_type就是DEPENDENT UNION。
第一個子查詢的select_type則是DEPENDENT SUBQUERY。
mysql> explain select * from t_order where order_id in (select order_id from t_order where order_id=100 union select order_id from t_order where order_id=200); +----+--------------------+------------+-------+---------------+---------+---------+-------+--------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+--------------------+------------+-------+---------------+---------+---------+-------+--------+-------------+ | 1 | PRIMARY | t_order | ALL | NULL | NULL | NULL | NULL | 100453 | Using where | | 2 | DEPENDENT SUBQUERY | t_order | const | PRIMARY | PRIMARY | 4 | const | 1 | Using index | | 3 | DEPENDENT UNION | t_order | const | PRIMARY | PRIMARY | 4 | const | 1 | Using index | | NULL | UNION RESULT | <union2,3> | ALL | NULL | NULL | NULL | NULL | NULL | | +----+--------------------+------------+-------+---------------+---------+---------+-------+--------+-------------+ 4 rows in set (0.03 sec)
3.SUBQUERY:
子查詢中的第一個select其select_type爲SUBQUERY。
mysql> explain select * from t_order where order_id=(select order_id from t_order where order_id=100); +----+-------------+---------+-------+---------------+---------+---------+-------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+---------+-------+---------------+---------+---------+-------+------+-------------+ | 1 | PRIMARY | t_order | const | PRIMARY | PRIMARY | 4 | const | 1 | | | 2 | SUBQUERY | t_order | const | PRIMARY | PRIMARY | 4 | | 1 | Using index | +----+-------------+---------+-------+---------------+---------+---------+-------+------+-------------+ 2 rows in set (0.03 sec)
4.DERIVED:
當子查詢是from子句時,其select_type爲DERIVED。
mysql> explain select * from (select order_id from t_order where order_id=100) a; +----+-------------+------------+--------+---------------+---------+---------+------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+------------+--------+---------------+---------+---------+------+------+-------------+ | 1 | PRIMARY | <derived2> | system | NULL | NULL | NULL | NULL | 1 | | | 2 | DERIVED | t_order | const | PRIMARY | PRIMARY | 4 | | 1 | Using index | +----+-------------+------------+--------+---------------+---------+---------+------+------+-------------+ 2 rows in set (0.03 sec)
二.type的說明
1.system,const
見上面4.DERIVED的例子。其中第一行的type就是爲system,第二行是const,這兩種聯接類型是最快的。
2.eq_ref
在t_order表中的order_id是主鍵,t_order_ext表中的order_id也是主鍵,該表能夠認爲是訂單表的補充信息表,他們的關係是1對1,在下面的例子中能夠看到b表的鏈接類型是eq_ref,這是極快的聯接類型。
mysql> explain select * from t_order a,t_order_ext b where a.order_id=b.order_id; +----+-------------+-------+--------+---------------+---------+---------+-----------------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+--------+---------------+---------+---------+-----------------+------+-------------+ | 1 | SIMPLE | b | ALL | order_id | NULL | NULL | NULL | 1 | | | 1 | SIMPLE | a | eq_ref | PRIMARY | PRIMARY | 4 | test.b.order_id | 1 | Using where | +----+-------------+-------+--------+---------------+---------+---------+-----------------+------+-------------+ 2 rows in set (0.00 sec)
3.ref
下面的例子在上面的例子上略做了修改,加上了條件。此時b表的聯接類型變成了ref。由於全部與a表中order_id=100的匹配記錄都將會從b表獲取。這是比較常見的聯接類型。
mysql> explain select * from t_order a,t_order_ext b where a.order_id=b.order_id and a.order_id=100; +----+-------------+-------+-------+---------------+----------+---------+-------+------+-------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+-------+---------------+----------+---------+-------+------+-------+ | 1 | SIMPLE | a | const | PRIMARY | PRIMARY | 4 | const | 1 | | | 1 | SIMPLE | b | ref | order_id | order_id | 4 | const | 1 | | +----+-------------+-------+-------+---------------+----------+---------+-------+------+-------+ 2 rows in set (0.00 sec)
4.ref_or_null
user_id字段是一個能夠爲空的字段,並對該字段建立了一個索引。在下面的查詢中能夠看到聯接類型爲ref_or_null,這是mysql爲含有null的字段專門作的處理。在咱們的表設計中應當儘可能避免索引字段爲NULL,由於這會額外的耗費mysql的處理時間來作優化。
mysql> explain select * from t_order where user_id=100 or user_id is null; +----+-------------+---------+-------------+---------------+---------+---------+-------+-------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+---------+-------------+---------------+---------+---------+-------+-------+-------------+ | 1 | SIMPLE | t_order | ref_or_null | user_id | user_id | 5 | const | 50325 | Using where | +----+-------------+---------+-------------+---------------+---------+---------+-------+-------+-------------+ 1 row in set (0.00 sec)
5.index_merge
常常出如今使用一張表中的多個索引時。mysql會將多個索引合併在一塊兒,以下例:
mysql> explain select * from t_order where order_id=100 or user_id=10; +----+-------------+---------+-------------+-----------------+-----------------+---------+------+------+-------------------------------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+---------+-------------+-----------------+-----------------+---------+------+------+-------------------------------------------+ | 1 | SIMPLE | t_order | index_merge | PRIMARY,user_id | PRIMARY,user_id | 4,5 | NULL | 2 | Using union(PRIMARY,user_id); Using where | +----+-------------+---------+-------------+-----------------+-----------------+---------+------+------+-------------------------------------------+ 1 row in set (0.09 sec)
6.unique_subquery
該聯接類型用於替換value IN (SELECT primary_key FROM single_table WHERE some_expr)這樣的子查詢的ref。注意ref列,其中第二行顯示的是func,代表unique_subquery是一個函數,而不是一個普通的ref。
mysql> explain select * from t_order where order_id in (select order_id from t_order where user_id=10); +----+--------------------+---------+-----------------+-----------------+---------+---------+------+--------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+--------------------+---------+-----------------+-----------------+---------+---------+------+--------+-------------+ | 1 | PRIMARY | t_order | ALL | NULL | NULL | NULL | NULL | 100649 | Using where | | 2 | DEPENDENT SUBQUERY | t_order | unique_subquery | PRIMARY,user_id | PRIMARY | 4 | func | 1 | Using where | +----+--------------------+---------+-----------------+-----------------+---------+---------+------+--------+-------------+ 2 rows in set (0.00 sec)
7.index_subquery
該聯接類型與上面的太像了,惟一的差異就是子查詢查的不是主鍵而是非惟一索引。
mysql> explain select * from t_order where user_id in (select user_id from t_order where order_id>10); +----+--------------------+---------+----------------+-----------------+---------+---------+------+--------+--------------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+--------------------+---------+----------------+-----------------+---------+---------+------+--------+--------------------------+ | 1 | PRIMARY | t_order | ALL | NULL | NULL | NULL | NULL | 100649 | Using where | | 2 | DEPENDENT SUBQUERY | t_order | index_subquery | PRIMARY,user_id | user_id | 5 | func | 50324 | Using index; Using where | +----+--------------------+---------+----------------+-----------------+---------+---------+------+--------+--------------------------+ 2 rows in set (0.00 sec)
8.range
按指定的範圍進行檢索,很常見。
mysql> explain select * from t_order where user_id in (100,200,300); +----+-------------+---------+-------+---------------+---------+---------+------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+---------+-------+---------------+---------+---------+------+------+-------------+ | 1 | SIMPLE | t_order | range | user_id | user_id | 5 | NULL | 3 | Using where | +----+-------------+---------+-------+---------------+---------+---------+------+------+-------------+ 1 row in set (0.00 sec)
9.index
在進行統計時很是常見,此聯接類型實際上會掃描索引樹,僅比ALL快些。
mysql> explain select count(*) from t_order; +----+-------------+---------+-------+---------------+---------+---------+------+--------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+---------+-------+---------------+---------+---------+------+--------+-------------+ | 1 | SIMPLE | t_order | index | NULL | user_id | 5 | NULL | 100649 | Using index | +----+-------------+---------+-------+---------------+---------+---------+------+--------+-------------+ 1 row in set (0.00 sec)
10.ALL
完整的掃描全表,最慢的聯接類型,儘量的避免。
mysql> explain select * from t_order; +----+-------------+---------+------+---------------+------+---------+------+--------+-------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+---------+------+---------------+------+---------+------+--------+-------+ | 1 | SIMPLE | t_order | ALL | NULL | NULL | NULL | NULL | 100649 | | +----+-------------+---------+------+---------------+------+---------+------+--------+-------+ 1 row in set (0.00 sec)
三.extra的說明
1.Distinct
MySQL發現第1個匹配行後,中止爲當前的行組合搜索更多的行。對於此項沒有找到合適的例子,求指點。
2.Not exists
由於b表中的order_id是主鍵,不可能爲NULL,因此mysql在用a表的order_id掃描t_order表,並查找b表的行時,若是在b表發現一個匹配的行就再也不繼續掃描b了,由於b表中的order_id字段不可能爲NULL。這樣避免了對b表的屢次掃描。
mysql> explain select count(1) from t_order a left join t_order_ext b on a.order_id=b.order_id where b.order_id is null; +----+-------------+-------+-------+---------------+--------------+---------+-----------------+--------+--------------------------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+-------+---------------+--------------+---------+-----------------+--------+--------------------------------------+ | 1 | SIMPLE | a | index | NULL | express_type | 1 | NULL | 100395 | Using index | | 1 | SIMPLE | b | ref | order_id | order_id | 4 | test.a.order_id | 1 | Using where; Using index; Not exists | +----+-------------+-------+-------+---------------+--------------+---------+-----------------+--------+--------------------------------------+ 2 rows in set (0.01 sec)
3.Range checked for each record
這種狀況是mysql沒有發現好的索引可用,速度比沒有索引要快得多。
mysql> explain select * from t_order t, t_order_ext s where s.order_id>=t.order_id and s.order_id<=t.order_id and t.express_type>5; +----+-------------+-------+-------+----------------------+--------------+---------+------+------+------------------------------------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+-------+----------------------+--------------+---------+------+------+------------------------------------------------+ | 1 | SIMPLE | t | range | PRIMARY,express_type | express_type | 1 | NULL | 1 | Using where | | 1 | SIMPLE | s | ALL | order_id | NULL | NULL | NULL | 1 | Range checked for each record (index map: 0x1) | +----+-------------+-------+-------+----------------------+--------------+---------+------+------+------------------------------------------------+ 2 rows in set (0.00 sec)
4.Using filesort
在有排序子句的狀況下很常見的一種狀況。此時mysql會根據聯接類型瀏覽全部符合條件的記錄,並保存排序關鍵字和行指針,而後排序關鍵字並按順序檢索行。
mysql> explain select * from t_order order by express_type; +----+-------------+---------+------+---------------+------+---------+------+--------+----------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+---------+------+---------------+------+---------+------+--------+----------------+ | 1 | SIMPLE | t_order | ALL | NULL | NULL | NULL | NULL | 100395 | Using filesort | +----+-------------+---------+------+---------------+------+---------+------+--------+----------------+ 1 row in set (0.00 sec)
5.Using index
這是性能很高的一種狀況。當查詢所需的數據能夠直接從索引樹中檢索到時,就會出現。上面的例子中有不少這樣的例子,再也不多舉例了。
6.Using temporary
發生這種狀況通常都是須要進行優化的。mysql須要建立一張臨時表用來處理此類查詢。
mysql> explain select * from t_order a left join t_order_ext b on a.order_id=b.order_id group by b.order_id; +----+-------------+-------+------+---------------+----------+---------+-----------------+--------+---------------------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+------+---------------+----------+---------+-----------------+--------+---------------------------------+ | 1 | SIMPLE | a | ALL | NULL | NULL | NULL | NULL | 100395 | Using temporary; Using filesort | | 1 | SIMPLE | b | ref | order_id | order_id | 4 | test.a.order_id | 1 | | +----+-------------+-------+------+---------------+----------+---------+-----------------+--------+---------------------------------+ 2 rows in set (0.00 sec)
7.Using where
當有where子句時,extra都會有說明。
8.Using sort_union(...)/Using union(...)/Using intersect(...)
下面的例子中user_id是一個檢索範圍,此時mysql會使用sort_union函數來進行索引的合併。而當user_id是一個固定值時,請參看上面type說明5.index_merge的例子,此時會使用union函數進行索引合併。
mysql> explain select * from t_order where order_id=100 or user_id>10; +----+-------------+---------+-------------+-----------------+-----------------+---------+------+------+------------------------------------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+---------+-------------+-----------------+-----------------+---------+------+------+------------------------------------------------+ | 1 | SIMPLE | t_order | index_merge | PRIMARY,user_id | user_id,PRIMARY | 5,4 | NULL | 2 | Using sort_union(user_id,PRIMARY); Using where | +----+-------------+---------+-------------+-----------------+-----------------+---------+------+------+------------------------------------------------+ 1 row in set (0.00 sec)
對於Using intersect的例子能夠參看下例,user_id與express_type發生了索引交叉合併。
mysql> explain select * from t_order where express_type=1 and user_id=100; +----+-------------+---------+-------------+----------------------+----------------------+---------+------+------+----------------------------------------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+---------+-------------+----------------------+----------------------+---------+------+------+----------------------------------------------------+ | 1 | SIMPLE | t_order | index_merge | user_id,express_type | user_id,express_type | 5,1 | NULL | 1 | Using intersect(user_id,express_type); Using where | +----+-------------+---------+-------------+----------------------+----------------------+---------+------+------+----------------------------------------------------+ 1 row in set (0.00 sec)
9.Using index for group-by
代表能夠在索引中找到分組所需的全部數據,不須要查詢實際的表。
mysql> explain select user_id from t_order group by user_id; +----+-------------+---------+-------+---------------+---------+---------+------+------+--------------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+---------+-------+---------------+---------+---------+------+------+--------------------------+ | 1 | SIMPLE | t_order | range | NULL | user_id | 5 | NULL | 3 | Using index for group-by | +----+-------------+---------+-------+---------------+---------+---------+------+------+--------------------------+ 1 row in set (0.00 sec)
除了上面的三個說明,還須要注意rows的數值,多行之間的數值是乘積的關係,能夠估算大概要處理的行數,若是乘積很大,那就頗有優化的必要了。