1. 程式人生 > >mysql 優化 explain 和show warnings 用法

mysql 優化 explain 和show warnings 用法

explain顯示了mysql如何使用索引來處理select語句以及連線表。可以幫助選擇更好的索引和寫出更優化的查詢語句。

使用方法,在select語句前加上explain就可以了:

如:

  1. explain select surname,first_name form a,b where a.id=b.id 

EXPLAIN列的解釋:

table:顯示這一行的資料是關於哪張表的

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忽略索引

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:這個連線型別對於前面的每一個記錄聯合進行完全掃描,這一般比較糟糕,應該儘量避免

  

先看一個例子:

  1. mysql> explain select * from t_order; 
  2. +----+-------------+---------+------+---------------+------+---------+------+--------+-------+ 
  3. | id | select_type | table   | type | possible_keys | key  | key_len | ref  | rows   | Extra | 
  4. +----+-------------+---------+------+---------------+------+---------+------+--------+-------+ 
  5. |  1 | SIMPLE      | t_order | ALL  | NULL          | NULL | NULL    | NULL | 100453 |       | 
  6. +----+-------------+---------+------+---------------+------+---------+------+--------+-------+ 
  7. 1 row in set (0.03 sec) 

加上extended後之後:

  1. mysql> explain extended select * from t_order; 
  2. +----+-------------+---------+------+---------------+------+---------+------+--------+----------+-------+ 
  3. | id | select_type | table   | type | possible_keys | key  | key_len | ref  | rows   | filtered | Extra | 
  4. +----+-------------+---------+------+---------------+------+---------+------+--------+----------+-------+ 
  5. |  1 | SIMPLE      | t_order | ALL  | NULL          | NULL | NULL    | NULL | 100453 |   100.00 |       | 
  6. +----+-------------+---------+------+---------------+------+---------+------+--------+----------+-------+ 
  7. 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。

  1. mysql> explain select * from t_order where order_id=100 union select * from t_order where order_id=200; 
  2. +----+--------------+------------+-------+---------------+---------+---------+-------+------+-------+ 
  3. | id | select_type  | table      | type  | possible_keys | key     | key_len | ref   | rows | Extra | 
  4. +----+--------------+------------+-------+---------------+---------+---------+-------+------+-------+ 
  5. |  1 | PRIMARY      | t_order    | const | PRIMARY       | PRIMARY | 4       | const |    1 |       | 
  6. |  2 | UNION        | t_order    | const | PRIMARY       | PRIMARY | 4       | const |    1 |       | 
  7. | NULL | UNION RESULT | <union1,2> | ALL   | NULL          | NULL    | NULL    | NULL  | NULL |       | 
  8. +----+--------------+------------+-------+---------------+---------+---------+-------+------+-------+ 
  9. 3 rows in set (0.34 sec) 

2.DEPENDENT UNION與DEPENDENT SUBQUERY:

當union作為子查詢時,其中第二個union的select_type就是DEPENDENT UNION。
第一個子查詢的select_type則是DEPENDENT SUBQUERY。

  1. 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); 
  2. +----+--------------------+------------+-------+---------------+---------+---------+-------+--------+-------------+ 
  3. | id | select_type        | table      | type  | possible_keys | key     | key_len | ref   | rows   | Extra       | 
  4. +----+--------------------+------------+-------+---------------+---------+---------+-------+--------+-------------+ 
  5. |  1 | PRIMARY            | t_order    | ALL   | NULL          | NULL    | NULL    | NULL  | 100453 | Using where | 
  6. |  2 | DEPENDENT SUBQUERY | t_order    | const | PRIMARY       | PRIMARY | 4       | const |      1 | Using index | 
  7. |  3 | DEPENDENT UNION    | t_order    | const | PRIMARY       | PRIMARY | 4       | const |      1 | Using index | 
  8. | NULL | UNION RESULT       | <union2,3> | ALL   | NULL          | NULL    | NULL    | NULL  |   NULL |             | 
  9. +----+--------------------+------------+-------+---------------+---------+---------+-------+--------+-------------+ 
  10. 4 rows in set (0.03 sec) 

3.SUBQUERY:

子查詢中的第一個select其select_type為SUBQUERY。

  1. mysql> explain select * from t_order where order_id=(select order_id from t_order where order_id=100); 
  2. +----+-------------+---------+-------+---------------+---------+---------+-------+------+-------------+ 
  3. | id | select_type | table   | type  | possible_keys | key     | key_len | ref   | rows | Extra       | 
  4. +----+-------------+---------+-------+---------------+---------+---------+-------+------+-------------+ 
  5. |  1 | PRIMARY     | t_order | const | PRIMARY       | PRIMARY | 4       | const |    1 |             | 
  6. |  2 | SUBQUERY    | t_order | const | PRIMARY       | PRIMARY | 4       |       |    1 | Using index | 
  7. +----+-------------+---------+-------+---------------+---------+---------+-------+------+-------------+ 
  8. 2 rows in set (0.03 sec) 

4.DERIVED:

當子查詢是from子句時,其select_type為DERIVED。

  1. mysql> explain select * from (select order_id from t_order where order_id=100) a; 
  2. +----+-------------+------------+--------+---------------+---------+---------+------+------+-------------+ 
  3. | id | select_type | table      | type   | possible_keys | key     | key_len | ref  | rows | Extra       | 
  4. +----+-------------+------------+--------+---------------+---------+---------+------+------+-------------+ 
  5. |  1 | PRIMARY     | <derived2> | system | NULL          | NULL    | NULL    | NULL |    1 |             | 
  6. |  2 | DERIVED     | t_order    | const  | PRIMARY       | PRIMARY | 4       |      |    1 | Using index | 
  7. +----+-------------+------------+--------+---------------+---------+---------+------+------+-------------+ 
  8. 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,這是極快的聯接型別。

  1. mysql> explain select * from t_order a,t_order_ext b where a.order_id=b.order_id; 
  2. +----+-------------+-------+--------+---------------+---------+---------+-----------------+------+-------------+ 
  3. | id | select_type | table | type   | possible_keys | key     | key_len | ref             | rows | Extra       | 
  4. +----+-------------+-------+--------+---------------+---------+---------+-----------------+------+-------------+ 
  5. |  1 | SIMPLE      | b     | ALL    | order_id      | NULL    | NULL    | NULL            |    1 |             | 
  6. |  1 | SIMPLE      | a     | eq_ref | PRIMARY       | PRIMARY | 4       | test.b.order_id |    1 | Using where | 
  7. +----+-------------+-------+--------+---------------+---------+---------+-----------------+------+-------------+ 
  8. 2 rows in set (0.00 sec) 

3.ref

下面的例子在上面的例子上略作了修改,加上了條件。此時b表的聯接型別變成了ref。因為所有與a表中order_id=100的匹配記錄都將會從b表獲取。這是比較常見的聯接型別。

  1. mysql> explain select * from t_order a,t_order_ext b where a.order_id=b.order_id and a.order_id=100; 
  2. +----+-------------+-------+-------+---------------+----------+---------+-------+------+-------+ 
  3. | id | select_type | table | type  | possible_keys | key      | key_len | ref   | rows | Extra | 
  4. +----+-------------+-------+-------+---------------+----------+---------+-------+------+-------+ 
  5. |  1 | SIMPLE      | a     | const | PRIMARY       | PRIMARY  | 4       | const |    1 |       | 
  6. |  1 | SIMPLE      | b     | ref   | order_id      | order_id | 4       | const |    1 |       | 
  7. +----+-------------+-------+-------+---------------+----------+---------+-------+------+-------+ 
  8. 2 rows in set (0.00 sec) 

4.ref_or_null

user_id欄位是一個可以為空的欄位,並對該欄位建立了一個索引。在下面的查詢中可以看到聯接型別為ref_or_null,這是mysql為含有null的欄位專門做的處理。在我們的表設計中應當儘量避免索引欄位為NULL,因為這會額外的耗費mysql的處理時間來做優化。

  1. mysql> explain select * from t_order where user_id=100 or user_id is null; 
  2. +----+-------------+---------+-------------+---------------+---------+---------+-------+-------+-------------+ 
  3. | id | select_type | table   | type        | possible_keys | key     | key_len | ref   | rows  | Extra       | 
  4. +----+-------------+---------+-------------+---------------+---------+---------+-------+-------+-------------+ 
  5. |  1 | SIMPLE      | t_order | ref_or_null | user_id       | user_id | 5       | const | 50325 | Using where | 
  6. +----+-------------+---------+-------------+---------------+---------+---------+-------+-------+-------------+ 
  7. 1 row in set (0.00 sec) 

5.index_merge

經常出現在使用一張表中的多個索引時。mysql會將多個索引合併在一起,如下例:

  1. mysql> explain select * from t_order where order_id=100 or user_id=10; 
  2. +----+-------------+---------+-------------+-----------------+-----------------+---------+------+------+-------------------------------------------+ 
  3. | id | select_type | table   | type        | possible_keys   | key             | key_len | ref  | rows | Extra                                     | 
  4. +----+-------------+---------+-------------+-----------------+-----------------+---------+------+------+-------------------------------------------+ 
  5. |  1 | SIMPLE      | t_order | index_merge | PRIMARY,user_id | PRIMARY,user_id | 4,5     | NULL |    2 | Using union(PRIMARY,user_id); Using where | 
  6. +----+-------------+---------+-------------+-----------------+-----------------+---------+------+------+-------------------------------------------+ 
  7. 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。

  1. mysql> explain select * from t_order where order_id in (select order_id from t_order where user_id=10); 
  2. +----+--------------------+---------+-----------------+-----------------+---------+---------+------+--------+-------------+ 
  3. | id | select_type        | table   | type            | possible_keys   | key     | key_len | ref  | rows   | Extra       | 
  4. +----+--------------------+---------+-----------------+-----------------+---------+---------+------+--------+-------------+ 
  5. |  1 | PRIMARY            | t_order | ALL             | NULL            | NULL    | NULL    | NULL | 100649 | Using where | 
  6. |  2 | DEPENDENT SUBQUERY | t_order | unique_subquery | PRIMARY,user_id | PRIMARY | 4       | func |      1 | Using where | 
  7. +----+--------------------+---------+-----------------+-----------------+---------+---------+------+--------+-------------+ 
  8. 2 rows in set (0.00 sec) 

7.index_subquery

該聯接型別與上面的太像了,唯一的差別就是子查詢查的不是主鍵而是非唯一索引。

  1. mysql> explain select * from t_order where user_id in (select user_id from t_order where order_id>10); 
  2. +----+--------------------+---------+----------------+-----------------+---------+---------+------+--------+--------------------------+ 
  3. | id | select_type        | table   | type           | possible_keys   | key     | key_len | ref  | rows   | Extra                    | 
  4. +----+--------------------+---------+----------------+-----------------+---------+---------+------+--------+--------------------------+ 
  5. |  1 | PRIMARY            | t_order | ALL            | NULL            | NULL    | NULL    | NULL | 100649 | Using where              | 
  6. |  2 | DEPENDENT SUBQUERY | t_order | index_subquery | PRIMARY,user_id | user_id | 5       | func |  50324 | Using index; Using where | 
  7. +----+--------------------+---------+----------------+-----------------+---------+---------+------+--------+--------------------------+ 
  8. 2 rows in set (0.00 sec) 

8.range

按指定的範圍進行檢索,很常見。

  1. mysql> explain select * from t_order where user_id in (100,200,300); 
  2. +----+-------------+---------+-------+---------------+---------+---------+------+------+-------------+ 
  3. | id | select_type | table   | type  | possible_keys | key     | key_len | ref  | rows | Extra       | 
  4. +----+-------------+---------+-------+---------------+---------+---------+------+------+-------------+ 
  5. |  1 | SIMPLE      | t_order | range | user_id       | user_id | 5       | NULL |    3 | Using where | 
  6. +----+-------------+---------+-------+---------------+---------+---------+------+------+-------------+ 
  7. 1 row in set (0.00 sec) 

9.index

在進行統計時非常常見,此聯接型別實際上會掃描索引樹,僅比ALL快些。

  1. mysql> explain select count(*) from t_order; 
  2. +----+-------------+---------+-------+---------------+---------+---------+------+--------+-------------+ 
  3. | id | select_type | table   | type  | possible_keys | key     | key_len | ref  | rows   | Extra       | 
  4. +----+-------------+---------+-------+---------------+---------+---------+------+--------+-------------+ 
  5. |  1 | SIMPLE      | t_order | index | NULL          | user_id | 5       | NULL | 100649 | Using index | 
  6. +----+-------------+---------+-------+---------------+---------+---------+------+--------+-------------+ 
  7. 1 row in set (0.00 sec) 

10.ALL

完整的掃描全表,最慢的聯接型別,儘可能的避免。

  1. mysql> explain select * from t_order; 
  2. +----+-------------+---------+------+---------------+------+---------+------+--------+-------+ 
  3. | id | select_type | table   | type | possible_keys | key  | key_len | ref  | rows   | Extra | 
  4. +----+-------------+---------+------+---------------+------+---------+------+--------+-------+ 
  5. |  1 | SIMPLE      | t_order | ALL  | NULL          | NULL | NULL    | NULL | 100649 |       | 
  6. +----+-------------+---------+------+---------------+------+---------+------+--------+-------+ 
  7. 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表的多次掃描。

  1. 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;  
  2. +----+-------------+-------+-------+---------------+--------------+---------+-----------------+--------+--------------------------------------+ 
  3. | id | select_type | table | type  | possible_keys | key          | key_len | ref             | rows   | Extra                                | 
  4. +----+-------------+-------+-------+---------------+--------------+---------+-----------------+--------+--------------------------------------+ 
  5. |  1 | SIMPLE      | a     | index | NULL          | express_type | 1       | NULL            | 100395 | Using index                          | 
  6. |  1 | SIMPLE      | b     | ref   | order_id      | order_id     | 4       | test.a.order_id |      1 | Using where; Using index; Not exists | 
  7. +----+-------------+-------+-------+---------------+--------------+---------+-----------------+--------+--------------------------------------+ 
  8. 2 rows in set (0.01 sec) 

3.Range checked for each record

這種情況是mysql沒有發現好的索引可用,速度比沒有索引要快得多。

  1. 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; 
  2. +----+-------------+-------+-------+----------------------+--------------+---------+------+------+------------------------------------------------+ 
  3. | id | select_type | table | type  | possible_keys        | key          | key_len | ref  | rows | Extra                                          | 
  4. +----+-------------+-------+-------+----------------------+--------------+---------+------+------+------------------------------------------------+ 
  5. |  1 | SIMPLE      | t     | range | PRIMARY,express_type | express_type | 1       | NULL |    1 | Using where                                    | 
  6. |  1 | SIMPLE      | s     | ALL   | order_id             | NULL         | NULL    | NULL |    1 | Range checked for each record (index map: 0x1) | 
  7. +----+-------------+-------+-------+----------------------+--------------+---------+------+------+------------------------------------------------+ 
  8. 2 rows in set (0.00 sec)

4.Using filesort

在有排序子句的情況下很常見的一種情況。此時mysql會根據聯接型別瀏覽所有符合條件的記錄,並儲存排序關鍵字和行指標,然後排序關鍵字並按順序檢索行。

  1. mysql> explain select * from t_order order by express_type; 
  2. +----+-------------+---------+------+---------------+------+---------+------+--------+----------------+ 
  3. | id | select_type | table   | type | possible_keys | key  | key_len | ref  | rows   | Extra          | 
  4. +----+-------------+---------+------+---------------+------+---------+------+--------+----------------+ 
  5. |  1 | SIMPLE      | t_order | ALL  | NULL          | NULL | NULL    | NULL | 100395 | Using filesort | 
  6. +----+-------------+---------+------+---------------+------+---------+------+--------+----------------+ 
  7. 1 row in set (0.00 sec) 

5.Using index

這是效能很高的一種情況。當查詢所需的資料可以直接從索引樹中檢索到時,就會出現。上面的例子中有很多這樣的例子,不再多舉例了。

6.Using temporary

發生這種情況一般都是需要進行優化的。mysql需要建立一張臨時表用來處理此類查詢。

  1. 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; 
  2. +----+-------------+-------+------+---------------+----------+---------+-----------------+--------+---------------------------------+ 
  3. | id | select_type | table | type | possible_keys | key      | key_len | ref             | rows   | Extra                           | 
  4. +----+-------------+-------+------+---------------+----------+---------+-----------------+--------+---------------------------------+ 
  5. |  1 | SIMPLE      | a     | ALL  | NULL          | NULL     | NULL    | NULL            | 100395 | Using temporary; Using filesort | 
  6. |  1 | SIMPLE      | b     | ref  | order_id      | order_id | 4       | test.a.order_id |      1 |                                 | 
  7. +----+-------------+-------+------+---------------+----------+---------+-----------------+--------+---------------------------------+ 
  8. 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函式進行索引合併。

  1. mysql> explain select * from t_order where order_id=100 or user_id>10; 
  2. +----+-------------+---------+-------------+-----------------+-----------------+---------+------+------+------------------------------------------------+ 
  3. | id | select_type | table   | type        | possible_keys   | key             | key_len | ref  | rows | Extra                                          | 
  4. +----+-------------+---------+-------------+-----------------+-----------------+---------+------+------+------------------------------------------------+ 
  5. |  1 | SIMPLE      | t_order | index_merge | PRIMARY,user_id | user_id,PRIMARY | 5,4     | NULL |    2 | Using sort_union(user_id,PRIMARY); Using where | 
  6. +----+-------------+---------+-------------+-----------------+-----------------+---------+------+------+------------------------------------------------+ 
  7. 1 row in set (0.00 sec) 

對於Using intersect的例子可以參看下例,user_id與express_type發生了索引交叉合併。

  1. mysql> explain select * from t_order where express_type=1 and user_id=100; 
  2. +----+-------------+---------+-------------+----------------------+----------------------+---------+------+------+----------------------------------------------------+ 
  3. | id | select_type | table   | type        | possible_keys        | key                  | key_len | ref  | rows | Extra                                              | 
  4. +----+-------------+---------+-------------+----------------------+----------------------+---------+------+------+----------------------------------------------------+ 
  5. |  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 | 
  6. +----+-------------+---------+-------------+----------------------+----------------------+---------+------+------+----------------------------------------------------+ 
  7. 1 row in set (0.00 sec) 

9.Using index for group-by

表明可以在索引中找到分組所需的所有資料,不需要查詢實際的表。

  1. mysql> explain select user_id from t_order group by user_id; 
  2. +----+-------------+---------+-------+---------------+---------+---------+------+------+--------------------------+ 
  3. | id | select_type | table   | type  | possible_keys | key     | key_len | ref  | rows | Extra                    | 
  4. +----+-------------+---------+-------+---------------+---------+---------+------+------+--------------------------+ 
  5. |  1 | SIMPLE      | t_order | range | NULL          | user_id | 5       | NULL |    3 | Using index for group-by | 
  6. +----+-------------+---------+-------+---------------+---------+---------+------+------+--------------------------+ 
  7. 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 performanceExtended 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 performanceexplain 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 formanceexplain 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選項。此時才返回結果給請求者即使用者。

 

轉自:https://www.cnblogs.com/yy