explain顯示了mysql如何使用索引來處理select語句以及鏈接表。能夠幫助選擇更好的索引和寫出更優化的查詢語句。html
使用方法,在select語句前加上explain就能夠了:mysql
如:sql
- explain select surname,first_name form a,b where a.id=b.id
EXPLAIN列的解釋:express
table:顯示這一行的數據是關於哪張表的mysql優化
type:這是重要的列,顯示鏈接使用了何種類型。從最好到最差的鏈接類型爲const、eq_reg、ref、range、index和ALL函數
type顯示的是訪問類型,是較爲重要的一個指標,結果值從好到壞依次是: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忽略索引spa
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類型,能夠爲如下任何一種:
- SIMPLE:簡單SELECT(不使用UNION或子查詢)
- PRIMARY:最外面的SELECT
- UNION:UNION中的第二個或後面的SELECT語句
- DEPENDENT UNION:UNION中的第二個或後面的SELECT語句,取決於外面的查詢
- UNION RESULT:UNION 的結果
- SUBQUERY:子查詢中的第一個SELECT
- DEPENDENT SUBQUERY:子查詢中的第一個SELECT,取決於外面的查詢
- DERIVED:導出表的SELECT(FROM子句的子查詢)
|
table |
輸出的行所引用的表 |
type |
聯接類型。下面給出各類聯接類型,按照從最佳類型到最壞類型進行排序:
- system:表僅有一行(=系統表)。這是const聯接類型的一個特例。
- const:表最多有一個匹配行,它將在查詢開始時被讀取。由於僅有一行,在這行的列值可被優化器剩餘部分認爲是常數。const表很快,由於它們只讀取一次!
- eq_ref:對於每一個來自於前面的表的行組合,從該表中讀取一行。這多是最好的聯接類型,除了const類型。
- ref:對於每一個來自於前面的表的行組合,全部有匹配索引值的行將從這張表中讀取。
- ref_or_null:該聯接類型如同ref,可是添加了MySQL能夠專門搜索包含NULL值的行。
- index_merge:該聯接類型表示使用了索引合併優化方法。
- unique_subquery:該類型替換了下面形式的IN子查詢的ref: value IN (SELECT primary_key FROM single_table WHERE some_expr) unique_subquery是一個索引查找函數,能夠徹底替換子查詢,效率更高。
- index_subquery:該聯接類型相似於unique_subquery。能夠替換IN子查詢,但只適合下列形式的子查詢中的非惟一索引: value IN (SELECT key_column FROM single_table WHERE some_expr)
- range:只檢索給定範圍的行,使用一個索引來選擇行。
- index:該聯接類型與ALL相同,除了只有索引樹被掃描。這一般比ALL快,由於索引文件一般比數據文件小。
- ALL:對於每一個來自於先前的表的行組合,進行完整的表掃描。
|
possible_keys |
指出MySQL能使用哪一個索引在該表中找到行 |
key |
顯示MySQL實際決定使用的鍵(索引)。若是沒有選擇索引,鍵是NULL。 |
key_len |
顯示MySQL決定使用的鍵長度。若是鍵是NULL,則長度爲NULL。 |
ref |
顯示使用哪一個列或常數與key一塊兒從表中選擇行。 |
rows |
顯示MySQL認爲它執行查詢時必須檢查的行數。多行之間的數據相乘能夠估算要處理的行數。 |
filtered |
顯示了經過條件過濾出的行數的百分比估計值。 |
Extra |
該列包含MySQL解決查詢的詳細信息
- Distinct:MySQL發現第1個匹配行後,中止爲當前的行組合搜索更多的行。
- Not exists:MySQL可以對查詢進行LEFT JOIN優化,發現1個匹配LEFT JOIN標準的行後,再也不爲前面的的行組合在該表內檢查更多的行。
- range checked for each record (index map: #):MySQL沒有發現好的可使用的索引,但發現若是來自前面的表的列值已知,可能部分索引可使用。
- Using filesort:MySQL須要額外的一次傳遞,以找出如何按排序順序檢索行。
- Using index:從只使用索引樹中的信息而不須要進一步搜索讀取實際的行來檢索表中的列信息。
- Using temporary:爲了解決查詢,MySQL須要建立一個臨時表來容納結果。
- Using where:WHERE 子句用於限制哪個行匹配下一個表或發送到客戶。
- Using sort_union(...), Using union(...), Using intersect(...):這些函數說明如何爲index_merge聯接類型合併索引掃描。
- Using index for group-by:相似於訪問表的Using index方式,Using index for group-by表示MySQL發現了一個索引,能夠用來查 詢GROUP BY或DISTINCT查詢的全部列,而不要額外搜索硬盤訪問實際的表。
|
一.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的數值,多行之間的數值是乘積的關係,能夠估算大概要處理的行數,若是乘積很大,那就頗有優化的必要了。
mysql中有一個explain 命令能夠用來分析select 語句的運行效果,例如explain能夠得到select語句
使用的索引狀況、排序的狀況等等。除此之外,explain 的extended 擴展可以在本來explain的基礎
上額外的提供一些查詢優化的信息,這些信息能夠經過mysql的show warnings命令獲得。下面是一個最簡單的例子。
首先執行對想要分析的語句進行explain,並帶上extended選項
mysql> explain extended select * from account;
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: account
type: ALL
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: 1
filtered: 100.00
Extra:
1 row in set, 1 warning (0.00 sec)
接下來再執行Show Warnings
mysql> show warnings;
*************************** 1. row ***************************
Level: Note
Code: 1003
Message: select `dbunit`.`account`.`id` AS `id`,`dbunit`.`account`.`name` AS `name` from `dbunit`.`account`
1 row in set (0.00 sec)
從 show warnings的輸出結果中咱們能夠看到本來的select * 被mysql優化成了
select `dbunit`.`account`.`id` AS `id`,`dbunit`.`account`.`name` AS `name`。
explain extended 除了可以告訴咱們mysql的查詢優化能作什麼,同時也能告訴咱們mysql的
查詢優化作不了什麼。Mysql performance的Extended EXPLAIN這篇文中中做者就利用explain
extended +show warnings 找到了mysql查詢優化器中不能查詢優化的地方。
從 EXPLAIN extended SELECT * FROM sbtest WHERE id>5 AND id>6 AND c="a" AND pad=c
語句的輸出咱們得知mysql的查詢優化器不能將id>5 和 id>6 這兩個查詢條件優化合併成一個 id>6。
在mysql performance的explain extended文章中第三個例子和靜室的explain的extended選項文章中,
兩位做者也對explain extended作了進一步的實驗,從這個兩篇文中中咱們能夠得出結論是從
explain extend的輸出中,咱們能夠看到sql的執行方式,對於分析sql仍是頗有幫助的。
下面特別摘抄了靜室的explain的extended選項這篇文章中的內容
/******************************如下代碼和分析摘抄至靜室的explain的extended選項**************/
mysql>explain extended select * from t where a in (select b from i);
+----+--------------------+-------+------+
| id | select_type | table | type |
+----+--------------------+-------+------+
| 1 | PRIMARY | t | ALL |
| 2 | DEPENDENT SUBQUERY | i | ALL |
+----+--------------------+-------+------+
2 rows in set, 1 warning (0.01 sec)
子查詢看起來和外部的查詢沒有任何關係,爲何MySQL顯示的是DEPENDENT SUBQUERY,
和外部相關的查詢呢?從explain extended的結果咱們就能夠看出緣由了。
mysql>show warnings
*************************** 1. row ***************************
Level: Note
Code: 1003
Message: select `test`.`t`.`a` AS `a`,`test`.`t`.`b` AS `b`,`test`.`t`.`c` AS `c`
from `test`.`t` where
(`test`.`t`.`a`,
(select 1 AS `Not_used` from `test`.`i`
where ((`test`.`t`.`a`) = `test`.`i`.`b`)))
1 row in set (0.00 sec)
在這裏MySQL改寫了SQL,作了in的優化。
/******************************以上代碼和分析摘抄至靜室的explain的extended選項*********************/
不過須要注意的一點是從EXPLAIN extended +show warnings獲得「優化之後」的查詢語句
可能還不是最終優化執行的sql,或者說explain extended看到的信息還不足以說明mysql最
終對查詢語句優化的結果。一樣仍是mysql formance的explain Extended這篇文章的第二個
例子就說明了這種狀況
/*****************************************************************************************************/
mysql> EXPLAIN extended SELECT t1.id,t2.pad FROM sbtest t1, sbtest t2 WHERE t1.id=5
AND t2.k=t1.k;
+----+-------------+-------+-------+---------------+---------+---------+-------+-------+-------+
| id | select_type | TABLE | type | possible_keys | KEY | key_len | ref | rows | Extra |
+----+-------------+-------+-------+---------------+---------+---------+-------+-------+-------+
| 1 | SIMPLE | t1 | const | PRIMARY,k | PRIMARY | 4 | const | 1 | |
| 1 | SIMPLE | t2 | ref | k | k | 4 | const | 55561 | |
+----+-------------+-------+-------+---------------+---------+---------+-------+-------+-------+
2 rows IN SET, 1 warning (0.00 sec)
mysql> SHOW warnings
*************************** 1. row ***************************
Level: Note
Code: 1003
Message: SELECT `test`.`t1`.`id` AS `id`,`test`.`t2`.`pad` AS `pad` FROM `test`.`sbtest` `t1`
JOIN `test`.`sbtest` `t2` WHERE ((`test`.`t2`.`k` = `test`.`t1`.`k`) AND (`test`.`t1`.`id` = 5))
1 row IN SET (0.00 sec)
/*************************************************************************************************/
從Explain的結果中咱們能夠獲得t1表的查詢使用的是"const"類型,也就是說mysql查詢的時候
會先由t1.id=5 找到t1.k 再利用t1.k的值去t2表中查詢數據,很顯然這樣的查詢優化結果沒有在
接下來的Show Warings輸出中找到。
sql執行順序
(1)from
(3) join
(2) on
(4) where
(5)group by(開始使用select中的別名,後面的語句中均可以使用)
(6) avg,sum....
(7)having
(8) select
(9) distinct
(10) order by
從這個順序中咱們不難發現,全部的 查詢語句都是從from開始執行的,在執行過程當中,每一個步驟都會爲下一個步驟生成一個虛擬表,這個虛擬表將做爲下一個執行步驟的輸入。
第一步:首先對from子句中的前兩個表執行一個笛卡爾乘積,此時生成虛擬表 vt1(選擇相對小的表作基礎表)
第二步:接下來即是應用on篩選器,on 中的邏輯表達式將應用到 vt1 中的各個行,篩選出知足on邏輯表達式的行,生成虛擬表 vt2
第三步:若是是outer join 那麼這一步就將添加外部行,left outer jion 就把左表在第二步中過濾的添加進來,若是是right outer join 那麼就將右表在第二步中過濾掉的行添加進來,這樣生成虛擬表 vt3
第四步:若是 from 子句中的表數目多餘兩個表,那麼就將vt3和第三個錶鏈接從而計算笛卡爾乘積,生成虛擬表,該過程就是一個重複1-3的步驟,最終獲得一個新的虛擬表 vt3。
第五步:應用where篩選器,對上一步生產的虛擬表引用where篩選器,生成虛擬表vt4,在這有個比較重要的細節不得不說一下,對於包含outer join子句的查詢,就有一個讓人感到困惑的問題,到底在on篩選器仍是用where篩選器指定邏輯表達式呢?on和where的最大區別在於,若是在on應用邏輯表達式那麼在第三步outer join中還能夠把移除的行再次添加回來,而where的移除的最終的。舉個簡單的例子,有一個學生表(班級,姓名)和一個成績表(姓名,成績),我如今須要返回一個x班級的全體同窗的成績,可是這個班級有幾個學生缺考,也就是說在成績表中沒有記錄。爲了獲得咱們預期的結果咱們就須要在on子句指定學生和成績表的關係(學生.姓名=成績.姓名)那麼咱們是否發如今執行第二步的時候,對於沒有參加考試的學生記錄就不會出如今vt2中,由於他們被on的邏輯表達式過濾掉了,可是咱們用left outer join就能夠把左表(學生)中沒有參加考試的學生找回來,由於咱們想返回的是x班級的全部學生,若是在on中應用學生.班級='x'的話,left outer join會把x班級的全部學生記錄找回(感謝網友康欽謀__康欽苗的指正),因此只能在where篩選器中應用學生.班級='x' 由於它的過濾是最終的。
第六步:group by 子句將中的惟一的值組合成爲一組,獲得虛擬表vt5。若是應用了group by,那麼後面的全部步驟都只能獲得的vt5的列或者是聚合函數(count、sum、avg等)。緣由在於最終的結果集中只爲每一個組包含一行。這一點請牢記。
第七步:應用cube或者rollup選項,爲vt5生成超組,生成vt6.
第八步:應用having篩選器,生成vt7。having篩選器是第一個也是爲惟一一個應用到已分組數據的篩選器。
第九步:處理select子句。將vt7中的在select中出現的列篩選出來。生成vt8.
第十步:應用distinct子句,vt8中移除相同的行,生成vt9。事實上若是應用了group by子句那麼distinct是多餘的,緣由一樣在於,分組的時候是將列中惟一的值分紅一組,同時只爲每一組返回一行記錄,那麼因此的記錄都將是不相同的。
第十一步:應用order by子句。按照order_by_condition排序vt9,此時返回的一個遊標,而不是虛擬表。sql是基於集合的理論的,集合不會預先對他的行排序,它只是成員的邏輯集合,成員的順序是可有可無的。對錶進行排序的查詢能夠返回一個對象,這個對象包含特定的物理順序的邏輯組織。這個對象就叫遊標。正由於返回值是遊標,那麼使用order by 子句查詢不能應用於表表達式。排序是很須要成本的,除非你必需要排序,不然最好不要指定order by,最後,在這一步中是第一個也是惟一一個可使用select列表中別名的步驟。
第十二步:應用top選項。此時才返回結果給請求者即用戶。
轉自:http://www.javashuo.com/article/p-nbksvujx-g.html