MySQL EXPLAIN命令詳解學習(檢視執行計劃)

lhrbest發表於2016-09-16


 EXPLAIN 命令詳解


MySQL的EXPLAIN命令用於SQL語句的查詢執行計劃(QEP)。這條命令的輸出結果能夠讓我們瞭解MySQL 最佳化器是如何執行
SQL 語句的。這條命令並沒有提供任何調整建議,但它能夠提供重要的資訊幫助你做出調優決策。

1 語法
MySQL 的EXPLAIN 語法可以執行在SELECT 語句或者特定表上。如果作用在表上,那麼此命令等同於DESC 表命令。UPDATE
和DELETE 命令也需要進行效能改進,當這些命令不是直接在表的主碼上執行時,為了確保最最佳化的索引使用率,需要把它們改
寫成SELECT 語句(以便對它們執行EXPLAIN 命令)。請看下面的示例:

[sql] view plain copy
 print?
  1. UPDATE table1  
  2. SET col1 = X, col2 = Y  
  3. WHERE id1 = 9  
  4. AND dt >= '2010-01-01';  


這個UPDATE語句可以被重寫成為下面這樣的SELECT語句:

[sql] view plain copy
 print?
  1. SELECT col1, col2  
  2. FROM table1  
  3. WHERE id1 = 9  
  4. AND dt >= '2010-01-01';  


在5.6.10版本里面,是可以直接對dml語句進行explain分析操作的.

MySQL 最佳化器是基於開銷來工作的,它並不提供任何的QEP的位置。這意味著QEP 是在每條SQL 語句執行的時候動態地計
算出來的。在MySQL 儲存過程中的SQL 語句也是在每次執行時計算QEP 的。儲存過程快取僅僅解析查詢樹。

2 各列詳解
MySQL EXPLAIN命令能夠為SQL語句中的每個表生成以下資訊:

[sql] view plain copy
 print?
  1. mysql> EXPLAIN SELECT * FROM inventory WHERE item_id = 16102176\G;  
  2.   ********************* 1. row ***********************  
  3.   id: 1  
  4.   select_type: SIMPLE  
  5.   table: inventory  
  6.   type: ALL  
  7.   possible_keys: NULL  
  8.   keyNULL  
  9.   key_len: NULL  
  10.   ref: NULL  
  11.   rows: 787338  
  12.   Extra: Using where  



  
這個QEP 顯示沒有使用任何索引(也就是全表掃描)並且處理了大量的行來滿足查詢。對同樣一條SELECT 語句,一個最佳化過的QEP 如下所示:

[sql] view plain copy
 print?
  1. ********************* 1. row ***********************  
  2. id: 1  
  3. select_type: SIMPLE  
  4. table: inventory  
  5. type: ref  
  6. possible_keys: item_id  
  7. key: item_id  
  8. key_len: 4  
  9. ref: const  
  10. rows: 1  
  11. Extra:  


在這個QEP 中,我們看到使用了一個索引,且估計只有一行資料將被獲取。


QEP 中每個行的所有列表如下所示:
? id
? select_type
? table
? partitions(這一列只有在EXPLAIN PARTITIONS 語法中才會出現)
? possible_keys
? key
? key_len
? ref
? rows
? filtered(這一列只有在EXPLAINED EXTENDED 語法中才會出現)
? Extra

這些列展示了SELECT 語句對每一個表的QEP。一個表可能和一個物理模式表或者在SQL 執行時生成的內部臨時表(例如從子查詢或者合併操作會產生內部臨時表)相關聯。
可以參考MySQL Reference Manual 獲得更多資訊:http://dev.mysql.com/doc/refman/5.5/en/explain-output.html


2.1 key
 key 列指出最佳化器選擇使用的索引。一般來說SQL 查詢中的每個表都僅使用一個索引。也存在索引合併的少數例外情況,如給定表上用到了兩個或者更多索引。
 下面是QEP 中key 列的示例:
 key: item_id
 key: NULL
 key: first, last
 SHOW CREATE TABLE命令是最簡單的檢視錶和索引列細節的方式。和key 列相關的列還包括possible_keys、rows 以及key_len。


2.2 ROWS
 rows 列提供了試圖分析所有存在於累計結果集中的行數目的MySQL 最佳化器估計值。QEP 很容易描述這個很困難的統計量。
 查詢中總的讀運算元量是基於合併之前行的每一行的rows 值的連續積累而得出的。這是一種巢狀行演算法。
 
 以連線兩個表的QEP 為例。透過id=1 這個條件找到的第一行的rows 值為1,這等於對第一個表做了一次讀操作。第二行是
 透過id=2 找到的,rows 的值為5。這等於有5 次讀運算子合當前1 的積累量。參考兩個表,讀操作的總數目是6。在另一個QEP
 中,第一rows 的值是5,第二rows 的值是1。這等於第一個表有5 次讀操作,對5個積累量中每個都有一個讀操作。因此兩個表
 總的讀操作的次數是10(5+5)次。
 
 最好的估計值是1,一般來說這種情況發生在當尋找的行在表中可以透過主鍵或者唯一鍵找到的時候。
 在下面的QEP 中,外面的巢狀迴圈可以透過id=1 來找到,其估計的物理行數是1。第二個迴圈處理了10行。

[sql] view plain copy
 print?
  1. ********************* 1. row ***********************  
  2. id: 1  
  3. select_type: SIMPLE  
  4. table: p  
  5. type: const  
  6. possible_keys: PRIMARY  
  7. keyPRIMARY  
  8. key_len: 4  
  9. ref: const  
  10. rows: 1  
  11. Extra:  
  12. ********************* 2. row ***********************  
  13. id: 1  
  14. select_type: SIMPLE  
  15. table: c  
  16. type: ref  
  17. possible_keys: parent_id  
  18. key: parent_id  
  19. key_len: 4  
  20. ref: const  
  21. rows: 10  
  22. Extra:  


 可以使用SHOW STATUS 命令來檢視實際的行操作。這個命令可以提供最佳的確認物理行操作的方式。請看下面的示例:
[sql] view plain copy
 print?
  1. mysql> SHOW SESSION STATUS LIKE 'Handler_read%';  
  2.  +-----------------------+-------+  
  3.  | Variable_name         | Value |  
  4.  +-----------------------+-------+  
  5.  | Handler_read_first    | 0     |  
  6.  | Handler_read_key      | 0     |   
  7.  | Handler_read_last     | 0     |  
  8.  | Handler_read_next     | 0     |  
  9.  | Handler_read_prev     | 0     |  
  10.  | Handler_read_rnd      | 0     |  
  11.  | Handler_read_rnd_next | 11    |  
  12.  +-----------------------+-------+  
  13.  7 rows in set (0.00 sec)  


  
 在下一個QEP 中,透過id=1 找到的外層巢狀迴圈估計有160行。第二個迴圈估計有1 行。
[sql] view plain copy
 print?
  1. ********************* 1. row ***********************  
  2.  id: 1  
  3.  select_type: SIMPLE  
  4.  table: p  
  5.  type: ALL  
  6.  possible_keys: NULL  
  7.  keyNULL  
  8.  key_len: NULL  
  9.  ref: NULL  
  10.  rows: 160  
  11.  Extra:  
  12. ********************* 2. row ***********************  
  13.  id: 1  
  14.  select type: SIMPLE  
  15.  table: c  
  16.  type: ref  
  17.  possible_keys: PRIMARY,parent_id  
  18.  key: parent_id  
  19.  key_len: 4  
  20.  ref: test.p.parent_id  
  21.  rows: 1  
  22.  Extra: Using where  


 
 透過SHOW STATUS 命令可以檢視實際的行操作,該命令表明物理讀運算元量大幅增加。請看下面的示例:
[sql] view plain copy
 print?
  1. mysql> SHOW SESSION STATUS LIKE 'Handler_read%';  
  2. +--------------------------------------+---------+  
  3. | Variable_name | Value |  
  4. +--------------------------------------+---------+  
  5. | Handler_read_first | 1 |  
  6. | Handler_read_key | 164 |  
  7. | Handler_read_last | 0 |  
  8. | Handler_read_next | 107 |  
  9. | Handler_read_prev | 0 |  
  10. | Handler_read_rnd | 0 |  
  11. | Handler_read_rnd_next | 161 |  
  12. +--------------------------------------+---------+  
  13. 相關的QEP 列還包括key列。  


 
 2.3 possible_keys
 possible_keys 列指出最佳化器為查詢選定的索引。
 一個會列出大量可能的索引(例如多於3 個)的QEP 意味著備選索引數量太多了,同時也可能提示存在一個無效的單列索引。
 可以用第2 章詳細介紹過的SHOW INDEXES 命令來檢查索引是否有效且是否具有合適的基數。
 為查詢確定QEP 的速度也會影響到查詢的效能。如果發現有大量的可能的索引,則意味著這些索引沒有被使用到。
 相關的QEP 列還包括key 列。
 
 2.4 key_len
 key_len 列定義了用於SQL 語句的連線條件的鍵的長度。此列值對於確認索引的有效性以及多列索引中用到的列的數目很重要。
 

常見的計算規律為:

① 1utf8字符集的字元佔用3個位元組;1gbk字符集的字元佔用2個位元組。

② 對於變長的型別(VARCHAR),key_len還要加2位元組;若欄位允許為空,則key_len需要加1

③ INT型別的長度為4

④ 對於DATATIME型別的欄位,在MySQL 5.6.4以前是8個位元組(不能儲存小數位),之後的長度為5個位元組再加上小數位位元組數。DATATIME最大小數位是6。若小數位為12,則總位元組數為65+1);若小數位為34,則總位元組數為75+2);若小數位為56,則總位元組數為85+3)。



此列的一些示例值如下所示:

 
 此列的一些示例值如下所示:
 key_len: 4 // INT NOT NULL
 key_len: 5 // INT NULL
 key_len: 30 // CHAR(30) NOT NULL
 key_len: 32 // VARCHAR(30) NOT NULL
 key_len: 92 // VARCHAR(30) NULL CHARSET=utf8
 
 從這些示例中可以看出,是否可以為空、可變長度的列以及key_len 列的值只和用在連線和WHERE 條件中的索引的列
 有關。索引中的其他列會在ORDER BY 或者GROUP BY 語句中被用到。下面這個來自於著名的開源部落格軟體WordPress 的表展示了
 如何以最佳方式使用帶有定義好的表索引的SQL 語句:
[sql] view plain copy
 print?
  1. CREATE TABLE `wp_posts` (  
  2.  `ID` bigint(20) unsigned NOT NULL AUTO_INCREMENT,  
  3.  `post_date` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',  
  4.  `post_status` varchar(20) NOT NULL DEFAULT 'publish' ,  
  5.  `post_type` varchar(20) NOT NULL DEFAULT 'post',  
  6.  PRIMARY KEY (`ID`),  
  7.  KEY `type_status_date`(`post_type`,`post_status`,`post_date`,`ID`)  
  8. DEFAULT CHARSET=utf8  
  9.   
  10.  CREATE TABLE `wp_posts` (  
  11.  `ID` bigint(20) unsigned NOT NULL AUTO_INCREMENT,  
  12.  `post_date` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',  
  13.  `post_status` varchar(20) NOT NULL DEFAULT 'publish' ,  
  14.  `post_type` varchar(20) NOT NULL DEFAULT 'post',  
  15.  PRIMARY KEY (`ID`),  
  16.  KEY `type_status_date`(`post_type`,`post_status`,`post_date`,`ID`)  
  17. DEFAULT CHARSET=utf8  
 這個表的索引包括post_type、post_status、post_date 以及ID列。下面是一個演示索引列用法的SQL 查詢:
 EXPLAIN SELECT ID, post_title FROM wp_posts WHERE post_type='post' AND post_date > '2010-06-01';
 
 這個查詢的QEP 返回的key_len 是62。這說明只有post_type列上的索引用到了(因為(20×3)+2=62)。儘管查詢在WHERE 語句
 中使用了post_type 和post_date 列,但只有post_type 部分被用到了。其他索引沒有被使用的原因是MySQL 只能使用定義索引的
 最左邊部分。為了更好地利用這個索引,可以修改這個查詢來調整索引的列。請看下面的示例:



[sql] view plain copy
 print?
  1. mysql> EXPLAIN SELECT ID, post_title  
  2. -> FROM wp_posts  
  3. -> WHERE post_type='post'  
  4. -> AND post_status='publish'  
  5. -> AND post_date > '2010-06-01';  
 
 在SELECT查詢的新增一個post_status 列的限制條件後,QEP顯示key_len 的值為132,這意味著post_type、post_status、post_date
 三列(62+62+8,(20×3)+2,(20×3)+2,8)都被用到了。此外,這個索引的主碼列ID 的定義是使用MyISAM 儲存索
 引的遺留痕跡。當使用InnoDB 儲存引擎時,在非主碼索引中包含主碼列是多餘的,這可以從key_len 的用法看出來。
 相關的QEP 列還包括帶有Using index 值的Extra 列。
 
 2.5 table
 table 列是EXPLAIN 命令輸出結果中的一個單獨行的唯一識別符號。這個值可能是表名、表的別名或者一個為查詢產生臨時表
 的識別符號,如派生表、子查詢或集合。下面是QEP 中table 列的一些示例:
 table: item
 table:
 table:
 表中N 和M 的值參考了另一個符合id 列值的table 行。相關的QEP 列還有select_type


 2.6 select_type
 select_type 列提供了各種表示table 列引用的使用方式的型別。最常見的值包括SIMPLE、PRIMARY、DERIVED 和UNION。其他可能
 的值還有UNION RESULT、DEPENDENT SUBQUERY、DEPENDENT UNION、UNCACHEABLE UNION 以及UNCACHEABLE QUERY。

 1. SIMPLE
 對於不包含子查詢和其他複雜語法的簡單查詢,這是一個常 見的型別。


 2. PRIMARY
 這是為更復雜的查詢而建立的首要表(也就是最外層的表)。這個型別通常可以在DERIVED 和UNION 型別混合使用時見到。
 
 3. DERIVED
 當一個表不是一個物理表時,那麼就被叫做DERIVED。下面的SQL 語句給出了一個QEP 中DERIVED select-type 型別的
 示例:
 mysql> EXPLAIN SELECT MAX(id)
 -> FROM (SELECT id FROM users WHERE first = 'west') c;


 4. DEPENDENT SUBQUERY
 這個select-type 值是為使用子查詢而定義的。下面的SQL語句提供了這個值:
 mysql> EXPLAIN SELECT p.*
 -> FROM parent p
 -> WHERE p.id NOT IN (SELECT c.parent_id FROM child c);
 
 5. UNION
 這是UNION 語句其中的一個SQL 元素。
 6. UNION RESULT
 這是一系列定義在UNION 語句中的表的返回結果。當select_type 為這個值時,經常可以看到table 的值是
 這說明匹配的id 行是這個集合的一部分。下面的SQL產生了一個UNION和UNION RESULT select-type:
 mysql> EXPLAIN SELECT p.* FROM parent p WHERE p.val
 LIKE 'a%'
 -> UNION
 -> SELECT p.* FROM parent p WHERE p.id > 5;

 2.7  partitions
  partitions 列代表給定表所使用的分割槽。這一列只會在EXPLAIN
  PARTITIONS 語句中出現。

 2.8 Extra
 Extra 列提供了有關不同種類的MySQL 最佳化器路徑的一系列
 額外資訊。Extra 列可以包含多個值,可以有很多不同的取值,並
 且這些值還在隨著MySQL 新版本的釋出而進一步增加。下面給
 出常用值的列表。你可以從下面的地址找到更全面的值的列表:
 http://dev.mysql.com/doc/refman/5.5/en/explain-output.html

 1. Using where
 這個值表示查詢使用了where 語句來處理結果——例如執行
 全表掃描。如果也用到了索引,那麼行的限制條件是透過獲取必
 要的資料之後處理讀緩衝區來實現的。

 2. Using temporary
 這個值表示使用了內部臨時(基於記憶體的)表。一個查詢可能
 用到多個臨時表。有很多原因都會導致MySQL 在執行查詢期間
 建立臨時表。兩個常見的原因是在來自不同表的列上使用了
 DISTINCT,或者使用了不同的ORDER BY 和GROUP BY 列。
 想了解更多內容可以訪問_
 of_query_execution_and_use_of_temp_tables。
 可以強制指定一個臨時表使用基於磁碟的MyISAM 儲存引
 擎。這樣做的原因主要有兩個:
 ? 內部臨時表佔用的空間超過min(tmp_table_size,max_
 heap_table_size)系統變數的限制
 ? 使用了TEXT/BLOB 列

 3. Using filesort
 這是ORDER BY 語句的結果。這可能是一個CPU 密集型的過程。
 可以透過選擇合適的索引來改進效能,用索引來為查詢結果排序。詳細過程請參考第4 章。

 4. Using index
 這個值重點強調了只需要使用索引就可以滿足查詢表的要求,不需要直接訪問表資料。請參考第5 章的詳細示例來理解這
 個值。

 5. Using join buffer
 這個值強調了在獲取連線條件時沒有使用索引,並且需要連線緩衝區來儲存中間結果。
 如果出現了這個值,那應該注意,根據查詢的具體情況可能需要新增索引來改進效能。

 6. Impossible where
 這個值強調了where 語句會導致沒有符合條件的行。請看下面的示例:
 mysql> EXPLAIN SELECT * FROM user WHERE 1=2;

 7. Select tables optimized away
 這個值意味著僅透過使用索引,最佳化器可能僅從聚合函式結果中返回一行。請看下面的示例:

 8. Distinct
 這個值意味著MySQL 在找到第一個匹配的行之後就會停止搜尋其他行。

 9. Index merges
 當MySQL 決定要在一個給定的表上使用超過一個索引的時候,就會出現以下格式中的一個,詳細說明使用的索引以及合併的型別。
 ? Using sort_union(...)
 ? Using union(...)
 ? Using intersect(...)

 2.9 id
 id 列是在QEP 中展示的表的連續引用。

 2.10 ref
 ref 列可以被用來標識那些用來進行索引比較的列或者常量。

 2.11 filtered
 filtered 列給出了一個百分比的值,這個百分比值和rows 列的值一起使用,可以估計出那些將要和QEP 中的前一個表進行連
 接的行的數目。前一個表就是指id 列的值比當前表的id 小的表。這一列只有在EXPLAIN EXTENDED 語句中才會出現。

 2.12 type
 type 列代表QEP 中指定的表使用的連線方式。下面是最常用的幾種連線方式:
 ? const 當這個表最多隻有一行匹配的行時出現system 這是const 的特例,當表只有一個row 時會出現
 ? eq_ref 這個值表示有一行是為了每個之前確定的表而讀取的
 ? ref 這個值表示所有具有匹配的索引值的行都被用到
 ? range 這個值表示所有符合一個給定範圍值的索引行都被用到
 ? ALL 這個值表示需要一次全表掃描其他型別的值還有fulltext 、ref_or_null 、index_merge 、unique_subquery、index_subquery 以及index。
  想了解更多資訊可以訪問http://dev.mysql.com/doc/refman/5.5/en/explain-output.html

 3 解釋EXPLAIN 輸出結果
 理解你的應用程式(包括技術和實現可能性)和最佳化SQL 語句同等重要。下面給出一個從父子關係中獲取孤立的父輩記錄的商
 業需求的例子。這個查詢可以用三種不同的方式構造。儘管會產生相同的結果,但QEP 會顯示三種不同的路徑。

[sql] view plain copy
 print?
  1. mysql> EXPLAIN SELECT p.*  
  2. -> FROM parent p  
  3. -> WHERE p.id NOT IN (SELECT c.parent_id FROM child  
  4. c)\G  
  5. ********************* 1. row ***********************  
  6. id: 1  
  7. select type: PRIMARY  
  8. table: p  
  9. type: ALL  
  10. possible_keys: NULL  
  11. keyNULL  
  12. key_len: NULL  
  13. ref: NULL  
  14. rows: 160  
  15. Extra: Using where  
  16. ********************* 2. row ***********************  
  17. id: 2  
  18. select_type: DEPENDENT SUBQUERY  
  19. table: c  
  20. type: index_subquery  
  21. possible_keys: parent_id  
  22. key: parent_id  
  23. key_len: 4  
  24. ref: func  
  25. rows: 1  
  26. Extra: Using index  
  27. rows in set (0.00 sec)  
  28.   
  29. EXPLAIN SELECT p.* FROM parent p LEFT JOIN child c ON p.id = c.parent_id WHERE c.child_id IS NULL\G  
  30. ********************* 1. row ***********************  
  31. id: 1  
  32. select_type: SIMPLE  
  33. table: p  
  34. type: ALL  
  35. possible_keys: NULL  
  36. keyNULL  
  37. key_len: NULL  
  38. ref: NULL  
  39. rows: 160  
  40. Extra:  
  41. ********************* 2. row ***********************  
  42. id: 1  
  43. select_type: SIMPLE  
  44. table: c  
  45. type: ref  
  46. possible_keys: parent_id  
  47. key: parent_id  
  48. key_len: 4  
  49. ref: test.p.id  
  50. rows: 1  
  51. Extra: Using where; Using indexNot exists  
  52. rows in set (0.00 sec)  





要使用執行計劃,首先要讀懂執行計劃,然後透過改寫SQL和索引技術來改進執行計劃。

MySQL5.6.3之前只有 SELECT 可以生成執行計劃,5.6.3及之後的版本SELECT DELETE INSERT REPLACE UPDATE都可以生成執行計劃。

explain語法:

{EXPLAIN | DESCRIBE | DESC} tbl_name [col_name | wild]

{EXPLAIN | DESCRIBE | DESC}
    [explain_type] explainable_stmt explain_type: {
    EXTENDED
  | PARTITIONS
  | FORMAT = format_name } format_name: {
    TRADITIONAL
  | JSON
} explainable_stmt: {
    SELECT statement
  | DELETE statement
  | INSERT statement
  | REPLACE statement
  | UPDATE statement
}
看到了吧,檢視執行計劃不只explain命令,desc也可以,結果一樣。


mysql> desc select * from p_range where id=12;
+----+-------------+---------+-------+---------------+---------+---------+-------+------+-------+
| id | select_type | table   | type  | possible_keys | key     | key_len | ref   | rows | Extra |
+----+-------------+---------+-------+---------------+---------+---------+-------+------+-------+
|  1 | SIMPLE      | p_range | const | PRIMARY       | PRIMARY | 4       | const |    1 | NULL  |
+----+-------------+---------+-------+---------------+---------+---------+-------+------+-------+
1 row in set (0.00 sec)


mysql> desc extended select * from p_range where id=12;
+----+-------------+---------+-------+---------------+---------+---------+-------+------+----------+-------+
| id | select_type | table   | type  | possible_keys | key     | key_len | ref   | rows |filtered | Extra |
+----+-------------+---------+-------+---------------+---------+---------+-------+------+----------+-------+
|  1 | SIMPLE      | p_range | const | PRIMARY       | PRIMARY | 4       | const |    1 |   100.00 | NULL  |
+----+-------------+---------+-------+---------------+---------+---------+-------+------+----------+-------+
1 row in set, 1 warning (0.02 sec)

有一個warning,可以看看

mysql> show warnings;
+-------+------+------------------------------------------------------------------------------------------+
| Level | Code | Message                                                                                  |
+-------+------+------------------------------------------------------------------------------------------+
| Note  | 1003 | /* select#1 */ select '12' AS `id`,'員工JONES' AS `name` from `test`.`p_range` where 1   |
+-------+------+------------------------------------------------------------------------------------------+
1 row in set (0.04 sec)
警告資訊顯示最佳化器最佳化後執行的SQL。再看一個複雜點的:

mysql> desc extended select * from emp where deptno in (select deptno from dept where deptno=20);
+----+-------------+-------+-------+---------------+---------+---------+-------+------+----------+-------------+
| id | select_type | table | type  | possible_keys | key     | key_len | ref   | rows | filtered | Extra       |
+----+-------------+-------+-------+---------------+---------+---------+-------+------+----------+-------------+
|  1 | SIMPLE      | dept  | const | PRIMARY       | PRIMARY | 1       | const |    1 |   100.00 | Using index |
|  1 | SIMPLE      | emp   | ALL   | NULL          | NULL    | NULL    | NULL  |   14 |   100.00 | Using where |
+----+-------------+-------+-------+---------------+---------+---------+-------+------+----------+-------------+
2 rows in set, 1 warning (0.00 sec)


mysql> show warnings;
+-------+------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Level | Code | Message                                                                                                                                                                                                                                                                                                                                                 |
+-------+------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Note  | 1003 | /* select#1 */ select `test`.`emp`.`empno` AS `empno`,`test`.`emp`.`ename` AS `ename`,`test`.`emp`.`job` AS `job`,`test`.`emp`.`mgr` AS `mgr`,`test`.`emp`.`hiredate` AS `hiredate`,`test`.`emp`.`sal` AS `sal`,`test`.`emp`.`comm` AS `comm`,`test`.`emp`.`deptno` AS `deptno` from `test`.`dept` join `test`.`emp` where (`test`.`emp`.`deptno` = 20) |
+-------+------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)

從警告裡可以看出最佳化器最終將*替換成所有的列名,這不但增加了sql文字的長度佔用更多記憶體,還會使返回的資料量增大,所以在select列表裡一定要寫明所選列的列名,尤其當表中列特別多時更應寫出列名,只選要檢視的列。



mysql> desc partitions select * from p_range where id=12;
+----+-------------+---------+------------+-------+---------------+---------+---------+-------+------+-------+
| id | select_type | table   | partitions | type  | possible_keys | key     | key_len | ref   | rows | Extra |
+----+-------------+---------+------------+-------+---------------+---------+---------+-------+------+-------+
|  1 | SIMPLE      | p_range | p0         | const | PRIMARY       | PRIMARY | 4       | const |    1 | NULL  |
+----+-------------+---------+------------+-------+---------------+---------+---------+-------+------+-------+
1 row in set (0.00 sec)


執行計劃的解釋可以參與這裡:http://dev.mysql.com/doc/refman/5.6/en/explain-output.html

以下摘錄一部分:

EXPLAIN Output Columns


This section describes the output columns produced by EXPLAIN. Later sections provide additional information about the type and Extra columns.

Each output row from EXPLAIN provides information about one table. Each row contains the values summarized in Table 8.1, “EXPLAIN Output Columns”, and described in more detail following the table. Column names are shown in the table's first column; the second column provides the equivalent property name shown in the output when FORMAT=JSON is used.

Table 8.1 EXPLAIN Output Columns

Column JSON Name Meaning
id select_id The SELECT identifier
select_type None The SELECT type
table table_name The table for the output row
partitions partitions The matching partitions
type access_type The join type
possible_keys possible_keys The possible indexes to choose
key key The index actually chosen
key_len key_length The length of the chosen key
ref ref The columns compared to the index
rows rows Estimate of rows to be examined
filtered filtered Percentage of rows filtered by table condition
Extra None Additional information

Note

JSON properties which are NULL are not displayed in JSON-formatted EXPLAIN output.

  •  id (JSON name: select_id)

    The SELECT identifier. This is the sequential number of the SELECT within the query. The value can be NULL if the row refers to the union result of other rows. In this case, the table column shows a value like <unionM,N> to indicate that the row refers to the union of the rows with id values of M and N.

  •  select_type (JSON name: none)

    The type of SELECT, which can be any of those shown in the following table. A JSON-formatted EXPLAIN exposes the SELECT type as a property of aquery_block, unless it is SIMPLE or PRIMARY. The JSON names (where applicable) are also shown in the table.

    select_type Value JSON Name Meaning
    SIMPLE None Simple SELECT (not using UNION or subqueries)
    PRIMARY None Outermost SELECT
    UNION None Second or later SELECT statement in a UNION
    DEPENDENT UNION dependent (true) Second or later SELECT statement in a UNION, dependent on outer query
    UNION RESULT union_result Result of a UNION.
    SUBQUERY None First SELECT in subquery
    DEPENDENT SUBQUERY dependent (true) First SELECT in subquery, dependent on outer query
    DERIVED None Derived table SELECT (subquery in FROM clause)
    MATERIALIZED materialized_from_subquery Materialized subquery
    UNCACHEABLE SUBQUERY cacheable (false) A subquery for which the result cannot be cached and must be re-evaluated for each row of the outer query
    UNCACHEABLE UNION cacheable (false) The second or later select in a UNION that belongs to an uncacheable subquery (seeUNCACHEABLE SUBQUERY)

    DEPENDENT typically signifies the use of a correlated subquery. See Section 13.2.10.7, “Correlated Subqueries”.

    DEPENDENT SUBQUERY evaluation differs from UNCACHEABLE SUBQUERY evaluation. For DEPENDENT SUBQUERY, the subquery is re-evaluated only once for each set of different values of the variables from its outer context. For UNCACHEABLE SUBQUERY, the subquery is re-evaluated for each row of the outer context.

    Cacheability of subqueries differs from caching of query results in the query cache (which is described in Section 8.10.3.1, “How the Query Cache Operates”). Subquery caching occurs during query execution, whereas the query cache is used to store results only after query execution finishes.

    When you specify FORMAT=JSON with EXPLAIN, the output has no single property directly equivalent to select_type; the query_block property corresponds to a given SELECT. Properties equivalent to most of the SELECT subquery types just shown are available (an example beingmaterialized_from_subquery for MATERIALIZED), and are displayed when appropriate. There are no JSON equivalents for SIMPLE or PRIMARY.

  •  table (JSON name: table_name)

    The name of the table to which the row of output refers. This can also be one of the following values:

    • <unionM,N>: The row refers to the union of the rows with id values of M and N.

    • <derivedN>: The row refers to the derived table result for the row with an id value of N. A derived table may result, for example, from a subquery in theFROM clause.

    • <subqueryN>: The row refers to the result of a materialized subquery for the row with an id value of N. See Section 8.2.1.18.2, “Optimizing Subqueries with Subquery Materialization”.

  •  partitions (JSON name: partitions)

    The partitions from which records would be matched by the query. This column is displayed only if the PARTITIONS keyword is used. The value is NULL for nonpartitioned tables. See Section 19.3.5, “Obtaining Information About Partitions”.

  •  type (JSON name: access_type)

    The join type. For descriptions of the different types, see EXPLAIN Join Types.

  •  possible_keys (JSON name: possible_keys)

    The possible_keys column indicates which indexes MySQL can choose from use to find the rows in this table. Note that this column is totally independent of the order of the tables as displayed in the output from EXPLAIN. That means that some of the keys in possible_keys might not be usable in practice with the generated table order.

    If this column is NULL (or undefined in JSON-formatted output), there are no relevant indexes. In this case, you may be able to improve the performance of your query by examining the WHERE clause to check whether it refers to some column or columns that would be suitable for indexing. If so, create an appropriate index and check the query with EXPLAIN again. See Section 13.1.7, “ALTER TABLE Syntax”.

    To see what indexes a table has, use SHOW INDEX FROM tbl_name.

  •  key (JSON name: key)

    The key column indicates the key (index) that MySQL actually decided to use. If MySQL decides to use one of the possible_keys indexes to look up rows, that index is listed as the key value.

    It is possible that key will name an index that is not present in the possible_keys value. This can happen if none of the possible_keys indexes are suitable for looking up rows, but all the columns selected by the query are columns of some other index. That is, the named index covers the selected columns, so although it is not used to determine which rows to retrieve, an index scan is more efficient than a data row scan.

    For InnoDB, a secondary index might cover the selected columns even if the query also selects the primary key because InnoDB stores the primary key value with each secondary index. If key is NULL, MySQL found no index to use for executing the query more efficiently.

    To force MySQL to use or ignore an index listed in the possible_keys column, use FORCE INDEXUSE INDEX, or IGNORE INDEX in your query. SeeSection 8.9.3, “Index Hints”.

    For MyISAM and NDB tables, running ANALYZE TABLE helps the optimizer choose better indexes. For NDB tables, this also improves performance of distributed pushed-down joins. For MyISAM tables, myisamchk --analyze does the same as ANALYZE TABLE. See Section 7.6, “MyISAM Table Maintenance and Crash Recovery”.

  •  key_len (JSON name: key_length)

    The key_len column indicates the length of the key that MySQL decided to use. The length is NULL if the key column says NULL. Note that the value ofkey_len enables you to determine how many parts of a multiple-part key MySQL actually uses.

  •  ref (JSON name: ref)

    The ref column shows which columns or constants are compared to the index named in the key column to select rows from the table.

    If the value is func, the value used is the result of some function. To see which function, use EXPLAIN EXTENDED followed by SHOW WARNINGS. The function might actually be an operator such as an arithmetic operator.

  •  rows (JSON name: rows)

    The rows column indicates the number of rows MySQL believes it must examine to execute the query.

    For InnoDB tables, this number is an estimate, and may not always be exact.

  •   filtered (JSON name: filtered)

    The filtered column indicates an estimated percentage of table rows that will be filtered by the table condition. That is, rows shows the estimated number of rows examined and rows × filtered / 100 shows the number of rows that will be joined with previous tables. This column is displayed if you useEXPLAIN EXTENDED.

  •  Extra (JSON name: none)

    This column contains additional information about how MySQL resolves the query. For descriptions of the different values, see EXPLAIN Extra Information.

    There is no single JSON property corresponding to the Extra column; however, values that can occur in this column are exposed as JSON properties, or as the text of the message property.

EXPLAIN Join Types

The type column of EXPLAIN output describes how tables are joined. In JSON-formatted output, these are found as values of the access_type property. The following list describes the join types, ordered from the best type to the worst:

  •  system

    The table has only one row (= system table). This is a special case of the const join type.

  •  const

    The table has at most one matching row, which is read at the start of the query. Because there is only one row, values from the column in this row can be regarded as constants by the rest of the optimizer. const tables are very fast because they are read only once.

    const is used when you compare all parts of a PRIMARY KEY or UNIQUE index to constant values. In the following queries, tbl_name can be used as aconst table:

    SELECT * FROM tbl_name WHERE primary_key=1;
    
    SELECT * FROM tbl_name WHERE primary_key_part1=1 AND primary_key_part2=2;
    
  •  eq_ref

    One row is read from this table for each combination of rows from the previous tables. Other than the system and const types, this is the best possible join type. It is used when all parts of an index are used by the join and the index is a PRIMARY KEY or UNIQUE NOT NULL index.

    eq_ref can be used for indexed columns that are compared using the = operator. The comparison value can be a constant or an expression that uses columns from tables that are read before this table. In the following examples, MySQL can use an eq_ref join to process ref_table:

    SELECT * FROM ref_table,other_table WHERE ref_table.key_column=other_table.column;
    
    SELECT * FROM ref_table,other_table WHERE ref_table.key_column_part1=other_table.column AND ref_table.key_column_part2=1;
    
  •  ref

    All rows with matching index values are read from this table for each combination of rows from the previous tables. ref is used if the join uses only a leftmost prefix of the key or if the key is not a PRIMARY KEY or UNIQUE index (in other words, if the join cannot select a single row based on the key value). If the key that is used matches only a few rows, this is a good join type.

    ref can be used for indexed columns that are compared using the = or <=> operator. In the following examples, MySQL can use a ref join to processref_table:

    SELECT * FROM ref_table WHERE key_column=expr;
    
    SELECT * FROM ref_table,other_table WHERE ref_table.key_column=other_table.column;
    
    SELECT * FROM ref_table,other_table WHERE ref_table.key_column_part1=other_table.column AND ref_table.key_column_part2=1;
    
  •  fulltext

    The join is performed using a FULLTEXT index.

  •  ref_or_null

    This join type is like ref, but with the addition that MySQL does an extra search for rows that contain NULL values. This join type optimization is used most often in resolving subqueries. In the following examples, MySQL can use a ref_or_null join to process ref_table:

    SELECT * FROM ref_table WHERE key_column=expr OR key_column IS NULL;
    

    See Section 8.2.1.8, “IS NULL Optimization”.

  •  index_merge

    This join type indicates that the Index Merge optimization is used. In this case, the key column in the output row contains a list of indexes used, and key_lencontains a list of the longest key parts for the indexes used. For more information, see Section 8.2.1.4, “Index Merge Optimization”.

  •  unique_subquery

    This type replaces eq_ref for some IN subqueries of the following form:

    value IN (SELECT primary_key FROM single_table WHERE some_expr)
    

    unique_subquery is just an index lookup function that replaces the subquery completely for better efficiency.

  •  index_subquery

    This join type is similar to unique_subquery. It replaces IN subqueries, but it works for nonunique indexes in subqueries of the following form:

    value IN (SELECT key_column FROM single_table WHERE some_expr)
    
  •  range

    Only rows that are in a given range are retrieved, using an index to select the rows. The key column in the output row indicates which index is used. Thekey_len contains the longest key part that was used. The ref column is NULL for this type.

    range can be used when a key column is compared to a constant using any of the =<>>>=<<=IS NULL<=>BETWEEN, or IN() operators:

    SELECT * FROM tbl_name WHERE key_column = 10;
    
    SELECT * FROM tbl_name WHERE key_column BETWEEN 10 and 20;
    
    SELECT * FROM tbl_name WHERE key_column IN (10,20,30);
    
    SELECT * FROM tbl_name WHERE key_part1 = 10 AND key_part2 IN (10,20,30);
    
  •  index

    The index join type is the same as ALL, except that the index tree is scanned. This occurs two ways:

    • If the index is a covering index for the queries and can be used to satisfy all data required from the table, only the index tree is scanned. In this case, theExtra column says Using index. An index-only scan usually is faster than ALL because the size of the index usually is smaller than the table data.

    • A full table scan is performed using reads from the index to look up data rows in index order. Uses index does not appear in the Extra column.

    MySQL can use this join type when the query uses only columns that are part of a single index.

  •  ALL

    A full table scan is done for each combination of rows from the previous tables. This is normally not good if the table is the first table not marked const, and usually very bad in all other cases. Normally, you can avoid ALL by adding indexes that enable row retrieval from the table based on constant values or column values from earlier tables.

EXPLAIN Extra Information

The Extra column of EXPLAIN output contains additional information about how MySQL resolves the query. The following list explains the values that can appear in this column. Each item also indicates for JSON-formatted output which property displays the Extra value. For some of these, there is a specific property. The others display as the text of the message property.

If you want to make your queries as fast as possible, look out for Extra column values of Using filesort and Using temporary, or, in JSON-formattedEXPLAIN output, for using_filesort and using_temporary_table properties equal to true.

  • Child of 'table' pushed join@1 (JSON: message text)

    This table is referenced as the child of table in a join that can be pushed down to the NDB kernel. Applies only in MySQL Cluster, when pushed-down joins are enabled. See the description of the ndb_join_pushdown server system variable for more information and examples.

  • const row not found (JSON property: const_row_not_found)

    For a query such as SELECT ... FROM tbl_name, the table was empty.

  • Deleting all rows (JSON property: message)

    For DELETE, some storage engines (such as MyISAM) support a handler method that removes all table rows in a simple and fast way. This Extra value is displayed if the engine uses this optimization.

  • Distinct (JSON property: distinct)

    MySQL is looking for distinct values, so it stops searching for more rows for the current row combination after it has found the first matching row.

  • FirstMatch(tbl_name) (JSON property: first_match)

    The semi-join FirstMatch join shortcutting strategy is used for tbl_name.

  • Full scan on NULL key (JSON property: message)

    This occurs for subquery optimization as a fallback strategy when the optimizer cannot use an index-lookup access method.

  • Impossible HAVING (JSON property: message)

    The HAVING clause is always false and cannot select any rows.

  • Impossible WHERE (JSON property: message)

    The WHERE clause is always false and cannot select any rows.

  • Impossible WHERE noticed after reading const tables (JSON property: message)

    MySQL has read all const (and system) tables and notice that the WHERE clause is always false.

  • LooseScan(m..n) (JSON property: message)

    The semi-join LooseScan strategy is used. m and n are key part numbers.

  • MaterializeScan (JSON: message text)

    Before MySQL 5.6.7, this indicates use of a single materialized temporary table. If Scan is present, no temporary table index is used for table reads. Otherwise, an index lookup is used. See also the Start materialize entry.

    As of MySQL 5.6.7, materialization is indicated by rows with a select_type value of MATERIALIZED and rows with a table value of <subqueryN>.

  • No matching min/max row (JSON property: message)

    No row satisfies the condition for a query such as SELECT MIN(...) FROM ... WHERE condition.

  • no matching row in const table (JSON property: message)

    For a query with a join, there was an empty table or a table with no rows satisfying a unique index condition.

  • No matching rows after partition pruning (JSON property: message)

    For DELETE or UPDATE, the optimizer found nothing to delete or update after partition pruning. It is similar in meaning to Impossible WHERE for SELECTstatements.

  • No tables used (JSON property: message)

    The query has no FROM clause, or has a FROM DUAL clause.

    For INSERT or REPLACE statements, EXPLAIN displays this value when there is no SELECT part. For example, it appears for EXPLAIN INSERT INTO t VALUES(10) because that is equivalent to EXPLAIN INSERT INTO t SELECT 10 FROM DUAL.

  • Not exists (JSON property: message)

    MySQL was able to do a LEFT JOIN optimization on the query and does not examine more rows in this table for the previous row combination after it finds one row that matches the LEFT JOIN criteria. Here is an example of the type of query that can be optimized this way:

    SELECT * FROM t1 LEFT JOIN t2 ON t1.id=t2.id
      WHERE t2.id IS NULL;
    

    Assume that t2.id is defined as NOT NULL. In this case, MySQL scans t1 and looks up the rows in t2 using the values of t1.id. If MySQL finds a matching row in t2, it knows that t2.id can never be NULL, and does not scan through the rest of the rows in t2 that have the same id value. In other words, for each row in t1, MySQL needs to do only a single lookup in t2, regardless of how many rows actually match in t2.

  • Range checked for each record (index map: N) (JSON property: message)

    MySQL found no good index to use, but found that some of indexes might be used after column values from preceding tables are known. For each row combination in the preceding tables, MySQL checks whether it is possible to use a range or index_merge access method to retrieve rows. This is not very fast, but is faster than performing a join with no index at all. The applicability criteria are as described in Section 8.2.1.3, “Range Optimization”, andSection 8.2.1.4, “Index Merge Optimization”, with the exception that all column values for the preceding table are known and considered to be constants.

    Indexes are numbered beginning with 1, in the same order as shown by SHOW INDEX for the table. The index map value N is a bitmask value that indicates which indexes are candidates. For example, a value of 0x19 (binary 11001) means that indexes 1, 4, and 5 will be considered.

  • Scanned N databases (JSON property: message)

    This indicates how many directory scans the server performs when processing a query for INFORMATION_SCHEMA tables, as described in Section 8.2.4, “Optimizing INFORMATION_SCHEMA Queries”. The value of N can be 0, 1, or all.

  • Select tables optimized away (JSON property: message)

    The optimizer determined 1) that at most one row should be returned, and 2) that to produce this row, a deterministic set of rows must be read. When the rows to be read can be read during the optimization phase (for example, by reading index rows), there is no need to read any tables during query execution.

    The first condition is fulfilled when the query is implicitly grouped (contains an aggregate function but no GROUP BY clause). The second condition is fulfilled when one row lookup is performed per index used. The number of indexes read determines the number of rows to read.

    Consider the following implicitly grouped query:

    SELECT MIN(c1), MIN(c2) FROM t1;
    

    Suppose that MIN(c1) can be retrieved by reading one index row and MIN(c2) can be retrieved by reading one row from a different index. That is, for each column c1 and c2, there exists an index where the column is the first column of the index. In this case, one row is returned, produced by reading two deterministic rows.

    This Extra value does not occur if the rows to read are not deterministic. Consider this query:

    SELECT MIN(c2) FROM t1 WHERE c1 <= 10;
    

    Suppose that (c1, c2) is a covering index. Using this index, all rows with c1 <= 10 must be scanned to find the minimum c2 value. By contrast, consider this query:

    SELECT MIN(c2) FROM t1 WHERE c1 = 10;
    

    In this case, the first index row with c1 = 10 contains the minimum c2 value. Only one row must be read to produce the returned row.

    For storage engines that maintain an exact row count per table (such as MyISAM, but not InnoDB), this Extra value can occur for COUNT(*) queries for which the WHERE clause is missing or always true and there is no GROUP BY clause. (This is an instance of an implicitly grouped query where the storage engine influences whether a deterministic number of rows can be read.)

  • Skip_open_tableOpen_frm_onlyOpen_trigger_onlyOpen_full_table (JSON property: message)

    These values indicate file-opening optimizations that apply to queries for INFORMATION_SCHEMA tables, as described in Section 8.2.4, “Optimizing INFORMATION_SCHEMA Queries”.

    • Skip_open_table: Table files do not need to be opened. The information has already become available within the query by scanning the database directory.

    • Open_frm_only: Only the table's .frm file need be opened.

    • Open_trigger_only: Only the table's .TRG file need be opened.

    • Open_full_table: The unoptimized information lookup. The .frm.MYD, and .MYI files must be opened.

  • Start materializeEnd materializeScan (JSON: message text)

    Before MySQL 5.6.7, this indicates use of multiple materialized temporary tables. If Scan is present, no temporary table index is used for table reads. Otherwise, an index lookup is used. See also the Materialize entry.

    As of MySQL 5.6.7, materialization is indicated by rows with a select_type value of MATERIALIZED and rows with a table value of <subqueryN>.

  • Start temporaryEnd temporary (JSON property: message)

    This indicates temporary table use for the semi-join Duplicate Weedout strategy.

  • unique row not found (JSON property: message)

    For a query such as SELECT ... FROM tbl_name, no rows satisfy the condition for a UNIQUE index or PRIMARY KEY on the table.

  • Using filesort (JSON property: using_filesort)

    MySQL must do an extra pass to find out how to retrieve the rows in sorted order. The sort is done by going through all rows according to the join type and storing the sort key and pointer to the row for all rows that match the WHERE clause. The keys then are sorted and the rows are retrieved in sorted order. SeeSection 8.2.1.15, “ORDER BY Optimization”.

  • Using index (JSON property: using_index)

    The column information is retrieved from the table using only information in the index tree without having to do an additional seek to read the actual row. This strategy can be used when the query uses only columns that are part of a single index.

    For InnoDB tables that have a user-defined clustered index, that index can be used even when Using index is absent from the Extra column. This is the case if type is index and key is PRIMARY.

  • Using index condition (JSON property: using_index_condition)

    Tables are read by accessing index tuples and testing them first to determine whether to read full table rows. In this way, index information is used to defer (push down) reading full table rows unless it is necessary. See Section 8.2.1.6, “Index Condition Pushdown Optimization”.

  • Using index for group-by (JSON property: using_index_for_group_by)

    Similar to the Using index table access method, Using index for group-by indicates that MySQL found an index that can be used to retrieve all columns of a GROUP BY or DISTINCT query without any extra disk access to the actual table. Additionally, the index is used in the most efficient way so that for each group, only a few index entries are read. For details, see Section 8.2.1.16, “GROUP BY Optimization”.

  • Using join buffer (Block Nested Loop)Using join buffer (Batched Key Access) (JSON property: using_join_buffer)

    Tables from earlier joins are read in portions into the join buffer, and then their rows are used from the buffer to perform the join with the current table.(Block Nested Loop) indicates use of the Block Nested-Loop algorithm and (Batched Key Access) indicates use of the Batched Key Access algorithm. That is, the keys from the table on the preceding line of the EXPLAIN output will be buffered, and the matching rows will be fetched in batches from the table represented by the line in which Using join buffer appears.

    In JSON-formatted output, the value of using_join_buffer is always either one of Block Nested Loop or Batched Key Access.

  • Using MRR (JSON property: message)

    Tables are read using the Multi-Range Read optimization strategy. See Section 8.2.1.13, “Multi-Range Read Optimization”.

  • Using sort_union(...)Using union(...)Using intersect(...) (JSON property: message)

    These indicate how index scans are merged for the index_merge join type. See Section 8.2.1.4, “Index Merge Optimization”.

  • Using temporary (JSON property: using_temporary_table)

    To resolve the query, MySQL needs to create a temporary table to hold the result. This typically happens if the query contains GROUP BY and ORDER BYclauses that list columns differently.

  • Using where (JSON property: attached_condition)

    WHERE clause is used to restrict which rows to match against the next table or send to the client. Unless you specifically intend to fetch or examine all rows from the table, you may have something wrong in your query if the Extra value is not Using where and the table join type is ALL or index.

    Using where has no direct counterpart in JSON-formatted output; the attached_condition property contains any WHERE condition used.

  • Using where with pushed condition (JSON property: message)

    This item applies to NDB tables only. It means that MySQL Cluster is using the Condition Pushdown optimization to improve the efficiency of a direct comparison between a nonindexed column and a constant. In such cases, the condition is pushed down to the cluster's data nodes and is evaluated on all data nodes simultaneously. This eliminates the need to send nonmatching rows over the network, and can speed up such queries by a factor of 5 to 10 times over cases where Condition Pushdown could be but is not used. For more information, see Section 8.2.1.5, “Engine Condition Pushdown Optimization”.




1)、id列數字越大越先執行,如果說數字一樣大,那麼就從上往下依次執行,id列為null的就表是這是一個結果集,不需要使用它來進行查詢。

 
2)、select_type列常見的有:
A:simple:表示不需要union操作或者不包含子查詢的簡單select查詢。有連線查詢時,外層的查詢為simple,且只有一個
B:primary:一個需要union操作或者含有子查詢的select,位於最外層的單位查詢的select_type即為primary。且只有一個
C:union:union連線的兩個select查詢,第一個查詢是dervied派生表,除了第一個表外,第二個以後的表select_type都是union
D:dependent union:與union一樣,出現在union 或union all語句中,但是這個查詢要受到外部查詢的影響
E:union result:包含union的結果集,在union和union all語句中,因為它不需要參與查詢,所以id欄位為null
F:subquery:除了from字句中包含的子查詢外,其他地方出現的子查詢都可能是subquery
G:dependent subquery:與dependent union類似,表示這個subquery的查詢要受到外部表查詢的影響
H:derived:from字句中出現的子查詢,也叫做派生表,其他資料庫中可能叫做內聯檢視或巢狀select
 
3)、table
顯示的查詢表名,如果查詢使用了別名,那麼這裡顯示的是別名,如果不涉及對資料表的操作,那麼這顯示為null,如果顯示為尖括號括起來的<derived N>就表示這個是臨時表,後邊的N就是執行計劃中的id,表示結果來自於這個查詢產生。如果是尖括號括起來的<union M,N>,與<derived N>類似,也是一個臨時表,表示這個結果來自於union查詢的id為M,N的結果集。
 
4)、type
依次從好到差:system,const,eq_ref,ref,fulltext,ref_or_null,unique_subquery,index_subquery,range,index_merge,index,ALL,除了all之外,其他的type都可以使用到索引,除了index_merge之外,其他的type只可以用到一個索引
A:system:表中只有一行資料或者是空表,且只能用於myisam和memory表。如果是Innodb引擎表,type列在這個情況通常都是all或者index
B:const:使用唯一索引或者主鍵,返回記錄一定是1行記錄的等值where條件時,通常type是const。其他資料庫也叫做唯一索引掃描
C:eq_ref:出現在要連線過個表的查詢計劃中,驅動表只返回一行資料,且這行資料是第二個表的主鍵或者唯一索引,且必須為not null,唯一索引和主鍵是多列時,只有所有的列都用作比較時才會出現eq_ref
D:ref:不像eq_ref那樣要求連線順序,也沒有主鍵和唯一索引的要求,只要使用相等條件檢索時就可能出現,常見與輔助索引的等值查詢。或者多列主鍵、唯一索引中,使用第一個列之外的列作為等值查詢也會出現,總之,返回資料不唯一的等值查詢就可能出現。
E:fulltext:全文索引檢索,要注意,全文索引的優先順序很高,若全文索引和普通索引同時存在時,mysql不管代價,優先選擇使用全文索引
F:ref_or_null:與ref方法類似,只是增加了null值的比較。實際用的不多。
G:unique_subquery:用於where中的in形式子查詢,子查詢返回不重複值唯一值
H:index_subquery:用於in形式子查詢使用到了輔助索引或者in常數列表,子查詢可能返回重複值,可以使用索引將子查詢去重。
I:range:索引範圍掃描,常見於使用>,<,is null,between ,in ,like等運算子的查詢中。
J:index_merge:表示查詢使用了兩個以上的索引,最後取交集或者並集,常見and ,or的條件使用了不同的索引,官方排序這個在ref_or_null之後,但是實際上由於要讀取所個索引,效能可能大部分時間都不如range
K:index:索引全表掃描,把索引從頭到尾掃一遍,常見於使用索引列就可以處理不需要讀取資料檔案的查詢、可以使用索引排序或者分組的查詢。
L:all:這個就是全表掃描資料檔案,然後再在server層進行過濾返回符合要求的記錄。
 
5)、possible_keys
查詢可能使用到的索引都會在這裡列出來
 
6)、key
查詢真正使用到的索引,select_type為index_merge時,這裡可能出現兩個以上的索引,其他的select_type這裡只會出現一個。
 
7)、key_len
用於處理查詢的索引長度,如果是單列索引,那就整個索引長度算進去,如果是多列索引,那麼查詢不一定都能使用到所有的列,具體使用到了多少個列的索引,這裡就會計算進去,沒有使用到的列,這裡不會計算進去。留意下這個列的值,算一下你的多列索引總長度就知道有沒有使用到所有的列了。要注意,mysql的ICP特性使用到的索引不會計入其中。另外,key_len只計算where條件用到的索引長度,而排序和分組就算用到了索引,也不會計算到key_len中。
 
8)、ref
如果是使用的常數等值查詢,這裡會顯示const,如果是連線查詢,被驅動表的執行計劃這裡會顯示驅動表的關聯欄位,如果是條件使用了表示式或者函式,或者條件列發生了內部隱式轉換,這裡可能顯示為func
 
9)、rows
這裡是執行計劃中估算的掃描行數,不是精確值
 
10)、extra
這個列可以顯示的資訊非常多,有幾十種,常用的有
A:distinct:在select部分使用了distinc關鍵字
B:no tables used:不帶from字句的查詢或者From dual查詢
C:使用not in()形式子查詢或not exists運算子的連線查詢,這種叫做反連線。即,一般連線查詢是先查詢內表,再查詢外表,反連線就是先查詢外表,再查詢內表。
D:using filesort:排序時無法使用到索引時,就會出現這個。常見於order by和group by語句中
E:using index:查詢時不需要回表查詢,直接透過索引就可以獲取查詢的資料。
F:using join buffer(block nested loop),using join buffer(batched key accss):5.6.x之後的版本最佳化關聯查詢的BNL,BKA特性。主要是減少內表的迴圈數量以及比較順序地掃描查詢。
G:using sort_union,using_union,using intersect,using sort_intersection:
using intersect:表示使用and的各個索引的條件時,該資訊表示是從處理結果獲取交集
using union:表示使用or連線各個使用索引的條件時,該資訊表示從處理結果獲取並集
using sort_union和using sort_intersection:與前面兩個對應的類似,只是他們是出現在用and和or查詢資訊量大時,先查詢主鍵,然後進行排序合併後,才能讀取記錄並返回。
H:using temporary:表示使用了臨時表儲存中間結果。臨時表可以是記憶體臨時表和磁碟臨時表,執行計劃中看不出來,需要檢視status變數,used_tmp_table,used_tmp_disk_table才能看出來。
I:using where:表示儲存引擎返回的記錄並不是所有的都滿足查詢條件,需要在server層進行過濾。查詢條件中分為限制條件和檢查條件,5.6之前,儲存引擎只能根據限制條件掃描資料並返回,然後server層根據檢查條件進行過濾再返回真正符合查詢的資料。5.6.x之後支援ICP特性,可以把檢查條件也下推到儲存引擎層,不符合檢查條件和限制條件的資料,直接不讀取,這樣就大大減少了儲存引擎掃描的記錄數量。extra列顯示using index condition
J:firstmatch(tb_name):5.6.x開始引入的最佳化子查詢的新特性之一,常見於where字句含有in()型別的子查詢。如果內表的資料量比較大,就可能出現這個
K:loosescan(m..n):5.6.x之後引入的最佳化子查詢的新特性之一,在in()型別的子查詢中,子查詢返回的可能有重複記錄時,就可能出現這個
 
除了這些之外,還有很多查詢資料字典庫,執行計劃過程中就發現不可能存在結果的一些提示資訊
 
11)、filtered
使用explain extended時會出現這個列,5.7之後的版本預設就有這個欄位,不需要使用explain extended了。這個欄位表示儲存引擎返回的資料在server層過濾後,剩下多少滿足查詢的記錄數量的比例,注意是百分比,不是具體記錄數。



可以使用EXPLAIN語句檢視SQL的執行計劃,執行計劃的資訊來自於最佳化器。

在MySQL 5.7,可以檢視SELECT, DELETE, INSERT, REPLACE, 和 UPDATE語句的執行計劃。

在MySQL 5.7.3,EXPLAIN EXTENDED可以獲取更詳細的執行計劃資訊,EXPLAIN PARTITIONS在獲取有分割槽表的執行計劃時很有用。

FORMAT選項可以用來選擇輸出的格式。TRADITIONAL代表以表格形式輸出,它是預設的輸出格式。JSON格式代表以JSON格式輸出,輸出結果中包含擴充套件的執行計劃資訊和分割槽表資訊。

如果索引沒有正常使用,可以透過執行ANALYZE TABLE命令來更新表的統計資訊,例如鍵值的cardinality,這會影響到最佳化器的選擇。

mysql> explain select * from emp e where e.deptno not in (select deptno from dept d);
+----+--------------------+-------+-----------------+---------------+---------+---------+------+------+-------------------------------------------------+
| id | select_type        | table | type            | possible_keys | key     | key_len | ref  | rows | Extra                                           |
+----+--------------------+-------+-----------------+---------------+---------+---------+------+------+-------------------------------------------------+
|  1 | PRIMARY            | e     | ALL             | NULL          | NULL    | NULL    | NULL |   14 | Using where                                     |
|  2 | DEPENDENT SUBQUERY | d     | unique_subquery | PRIMARY       | PRIMARY | 4       | func |    1 | Using index; Using where; Full scan on NULL key |
+----+--------------------+-------+-----------------+---------------+---------+---------+------+------+-------------------------------------------------+
2 rows in set (0.00 sec)

輸出欄位說明:

id     查詢的執行順序號
select_type     查詢型別,包括:

SIMPLE     簡單查詢(不使用UNION或子查詢)
PRIMARY     最外層的SELECT語句
UNION     在UNION結構中的第二個及以上的SELECT語句
DEPENDENT UNION      在UNION結構中的第二個及以上的SELECT語句,依賴外層查詢
UNION RESULT     UNION的結果
SUBQUERY     子查詢中的第一個SELECT語句
DEPENDENT SUBQUERY     子查詢中的第一個SELECT語句,依賴於外層查詢
DERIVED      子查詢中FROM後面的語句
MATERIALIZED     物化檢視子查詢
UNCACHEABLE SUBQUERY     查詢結果沒有被快取且需要重新外層查詢計算每行資料的子查詢
UNCACHEABLE UNION UNION    結構中第二個及之後的SELECT語句且沒有生成查詢快取

table     表名
type     表連線的型別,包括:

system     表中只有一行資料,這是cost連線型別的一種特殊情況
const     表中只有一行匹配記錄,且在查詢中被最先讀取
eq_ref     和之前的表作nested loop連線時,每次兩個表中連線欄位相比,都有一行匹配的記錄。當索引中的所有部分被用於連線且索引是主鍵索引或UNIQUE非空索引時,會使用這種型別。
ref     每次和之前的表做連線時,讀取所有符合條件的索引值。如果連線使用索引的最左邊字首欄位,或者索引不是主鍵或UNIQUE索引,會用到這種連線方式,也就是說如果連線不能基於每個符合連線條件的索引值選擇出單獨的一行,則會使用這種連線方式。
fulltext     使用FULLTEXT索引來建立連線
ref_or_null     連線型別類似ref,除此之外,MySQL會額外掃描出包含NULL值的行。這種連線方式通常用於有子查詢的情形下。
index_merge     使用索引合併的連線方式。在這種情況下,key欄位會包含使用的索引,key_len包含使用索引的最長索引部分。
unique_subquery      這種連線方式在某種情況下會代替eq_ref,如value IN (SELECT primary_key FROM single_table WHERE some_expr),這種方式使用索引查詢功能代替子查詢,以獲得更好的執行效率。
index_subquery      這種連線方式類似unique_subquery。它會代替IN子查詢,但是它適用於非unique索引的子查詢,如value IN (SELECT key_column FROM single_table WHERE some_expr)
range     使用索引掃描出指定範圍的行。key欄位指示使用的索引。key_len指示索引的最大長度。ref欄位會顯示NULL
index     這種索引連線型別和ALL相同,除了索引樹被掃描到。這會出現在兩種情況下:一、如果該索引是一個覆蓋索引查詢,且只掃描出索引樹。在這種情況下,Extra欄位會顯示Using index。二、透過索引順序來執行全表掃描。
ALL     和之前表做連線時,每次兩表關聯時都做全表掃描。

possible_keys     可供選擇的索引
key     實際選擇的索引
key_len      選擇的索引長度
ref     顯示和索引相比較的欄位或常量,如果這個欄位的值是func,這個值會用在函式的結果中。
rows     估計的表的行數
Extra     額外資訊

mysql> explain select * from buy_log where userid=2;
+----+-------------+---------+------+-----------------+--------+---------+-------+------+-------+
| id | select_type | table   | type | possible_keys   | key    | key_len | ref   | rows | Extra |
+----+-------------+---------+------+-----------------+--------+---------+-------+------+-------+
|  1 | SIMPLE      | buy_log | ref  | userid,userid_2 | userid | 4       | const |    1 | NULL  |
+----+-------------+---------+------+-----------------+--------+---------+-------+------+-------+
1 row in set (0.00 sec)

(04:14:50) [dmcdbMTNNG]> explain select count(*) from ADDSubscribers where timestamp between 1483351200 and 1483354800;
+----+-------------+----------------+-------+---------------+-----------+---------+------+---------+-----------------------------------+
| id | select_type | table          | type  | possible_keys | key       | key_len | ref  | rows    | Extra                             |
+----+-------------+----------------+-------+---------------+-----------+---------+------+---------+-----------------------------------+
|  1 | SIMPLE      | ADDSubscribers | range | Timestamp     | Timestamp | 8       | NULL | 3515427 | Using where with pushed condition |
+----+-------------+----------------+-------+---------------+-----------+---------+------+---------+-----------------------------------+
1 row in set (0.00 sec)

這個資訊只適用於NDB表。MySQL Cluster使用這個條件來提高效率。

Using where with pushed condition (JSON property: message)
This item applies to NDB tables only. It means that MySQL Cluster is using the Condition Pushdown
optimization to improve the efficiency of a direct comparison between a nonindexed column and a
constant. In such cases, the condition is “pushed down” to the cluster's data nodes and is evaluated on
all data nodes simultaneously. This eliminates the need to send nonmatching rows over the network, and
can speed up such queries by a factor of 5 to 10 times over cases where Condition Pushdown could be
but is not used. For more information, see Section 9.2.1.5, “Engine Condition Pushdown Optimization”.





MySQL執行計劃裡面的key_len

原創 2017-09-26 r13筆記地94天 楊建榮的學習筆記

  以前看MySQL的執行計劃,感覺內容有些簡陋,平時分析主要就是看是否全表掃描,索引使用是否合理等。基本上也能分析出很多問題來,但是顯然有時候會有些疑惑,那就是對於複合索引,多列值的情況下,到底啟用了那些索引列,這個時候索引的使用情況就很值得琢磨琢磨了,我們得根據執行計劃裡面的key_len做一個重要的參考。

   我們做一個簡單的測試來說明。

   CREATE TABLE `department` (

`DepartmentID` int(11) DEFAULT NULL,

`DepartmentName` varchar(20) DEFAULT NULL,

KEY `IND_D` (`DepartmentID`),

KEY `IND_DN` (`DepartmentName`)

) ENGINE=InnoDB DEFAULT CHARSET=gbk;

執行語句為:explain select count(*)from department\G

對於這個語句,key_len到底是多少呢?

mysql> explain select count(*)from department\G

*************************** 1. row ***************************

           id: 1

  select_type: SIMPLE

        table: department

         type: index

possible_keys: NULL

          key: IND_D

      key_len: 5

          ref: NULL

         rows: 1

        Extra: Using index

1 row in set (0.00 sec)

在這個例子裡面,possible_keys,key,Extra你看了可能有些暈,我們看看key_len的值為5,這個值是怎麼算出來的呢,首先表有兩個欄位,第一個欄位的型別為數值,int的長度為4,因為欄位可為null,所以需要一個位元組來儲存,這樣下來就是4+1=5了。由此我們可以看到這個語句是啟用了索引ind_d.

  那我們舉一反三,把語句修改一下,看看key_len的變化。

mysql>  explain select departmentName from department b where departmentName='TEST'\G

*************************** 1. row ***************************

           id: 1

  select_type: SIMPLE

        table: b

         type: ref

possible_keys: IND_DN

          key: IND_DN

      key_len: 43

          ref: const

         rows: 1

        Extra: Using where; Using index

1 row in set (0.09 sec)

從上面可以看到,key_len為43,這個值是怎麼算出來的呢,我們來掰扯一下,欄位2為字元型,長度20,因為是GBK字符集,所以需要乘以2,因為允許欄位為NULL,則需要一個位元組,對於變長的型別(在此就是VARCHAR),key_len還要加2位元組。這樣下來就是20*2+1+2=43

   到了這裡僅僅是個開始,我們需要看看略微複雜的情況,就需要複合索引了。我們就換一個表test_keylen2

create table test_keylen2 (c1 int not null,c2 int not null,c3 int not null);

alter table test_keylen2 add key  idx1(c1, c2, c3);

下面的語句就很實際了,

explain     SELECT *from test_keylen2 WHERE c1=1 AND c2=1 ORDER BY c1\G     

這個語句中,keylen到底是應該為4或者8還是12呢? 我們就需要驗證一下了。

mysql> explain     SELECT *from test_keylen2 WHERE c1=1 AND c2=1 ORDER BY c1\G     

*************************** 1. row ***************************

           id: 1

  select_type: SIMPLE

        table: test_keylen2

         type: ref

possible_keys: idx1

          key: idx1

      key_len: 8

          ref: const,const

         rows: 1

        Extra: Using index

1 row in set (0.07 sec)

顯然key_len只計算了where中涉及的列,因為是數值型別,所以就是4+4=8

那下面的這個語句呢。

explain   SELECT *from test_keylen2 WHERE c1>=1 and c2=2 \G  

我們新增一個範圍,看看這個該如何拆分。

mysql> explain   SELECT *from test_keylen2 WHERE c1>=1 and c2=2 \G  

*************************** 1. row ***************************

           id: 1

  select_type: SIMPLE

        table: test_keylen2

         type: index

possible_keys: idx1

          key: idx1

      key_len: 12

          ref: NULL

         rows: 1

        Extra: Using where; Using index

1 row in set (0.07 sec)

在這裡就不只是計算where中的列了,而是因為>1的條件直接選擇了3個列來計算。

  對於date型別的處理,有一個很細小的差別。我們再換一個表,含有事件型別的欄位,

CREATE TABLE `tmp_users` (

`id` int(11) NOT NULL

AUTO_INCREMENT,

`uid` int(11) NOT NULL,

`l_date` datetime NOT NULL,

`data` varchar(32) DEFAULT NULL,

PRIMARY KEY (`id`),

KEY `ind_uidldate` (`uid`,`l_date`)

) ENGINE=InnoDB DEFAULT CHARSET=gbk;

下面的語句key_len該如何計算呢。

explain select * from tmp_users where uid = 9527 and l_date >= '2012-12-10 10:13:17'\G

這一點出乎我的意料,按照datetime的印象是8個位元組,所以應該是8+4=12,但是這裡卻偏偏是9,這個數字怎麼計算的。

           id: 1

  select_type: SIMPLE

        table: tmp_users

         type: range

possible_keys: ind_uidldate

          key: ind_uidldate

      key_len: 9

          ref: NULL

         rows: 1

        Extra: Using index condition

1 row in set (0.07 sec)

這裡就涉及到一個技術細節,是在MySQL 5.6中的datetime的儲存差別。在5.6.4以前是8個位元組,之後是5個位元組(在不帶小數位的情況下)。

MySQL EXPLAIN命令詳解學習(檢視執行計劃)

請點選此處輸入圖片描述

所以按照這個演算法就是4+5=9




[root@LHRDB ~]# mysql -S/usr/local/mysql55/mysql5557/mysql.sock
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 1
Server version: 5.5.57 MySQL Community Server (GPL)


Copyright (c) 2000, 2017, Oracle and/or its affiliates. All rights reserved.


Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.


Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.


mysql> show database;
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'database' at line 1
mysql> show databases;


+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| test               |
+--------------------+
4 rows in set (2.57 sec)


mysql> 
mysql> 
mysql> 
mysql> use test;
Database changed
mysql> 
mysql> 
mysql> CREATE TABLE `tmp_users` (
    -> `id` int(11) NOT NULL
    -> AUTO_INCREMENT,
    -> `uid` int(11) NOT NULL,
    -> `l_date` datetime NOT NULL,
    -> `data` varchar(32) DEFAULT NULL,
    -> PRIMARY KEY (`id`),
    -> KEY `ind_uidldate` (`uid`,`l_date`)
    -> ) ENGINE=InnoDB DEFAULT CHARSET=gbk;
Query OK, 0 rows affected (0.06 sec)


mysql> explain select * from tmp_users where uid = 9527 and l_date >= '2012-12-10 10:13:17'\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: tmp_users
         type: range
possible_keys: ind_uidldate
          key: ind_uidldate
      key_len: 12
          ref: NULL
         rows: 1
        Extra: Using where
1 row in set (0.31 sec)






About Me

.............................................................................................................................................

● 本文作者:小麥苗,部分內容整理自網路,若有侵權請聯絡小麥苗刪除

● 本文在itpub(http://blog.itpub.net/26736162/abstract/1/)、部落格園(http://www.cnblogs.com/lhrbest)和個人微信公眾號(xiaomaimiaolhr)上有同步更新

● 本文itpub地址:http://blog.itpub.net/26736162/abstract/1/

● 本文部落格園地址:http://www.cnblogs.com/lhrbest

● 本文pdf版、個人簡介及小麥苗雲盤地址:http://blog.itpub.net/26736162/viewspace-1624453/

● 資料庫筆試面試題庫及解答:http://blog.itpub.net/26736162/viewspace-2134706/

● DBA寶典今日頭條號地址:

.............................................................................................................................................

● QQ群號:230161599(滿)、618766405

● 微信群:可加我微信,我拉大家進群,非誠勿擾

● 聯絡我請加QQ好友646634621,註明新增緣由

● 於 2017-08-01 09:00 ~ 2017-08-31 22:00 在魔都完成

● 文章內容來源於小麥苗的學習筆記,部分整理自網路,若有侵權或不當之處還請諒解

● 版權所有,歡迎分享本文,轉載請保留出處

.............................................................................................................................................

小麥苗的微店

小麥苗出版的資料庫類叢書http://blog.itpub.net/26736162/viewspace-2142121/

.............................................................................................................................................

使用微信客戶端掃描下面的二維碼來關注小麥苗的微信公眾號(xiaomaimiaolhr)及QQ群(DBA寶典),學習最實用的資料庫技術。

   小麥苗的微信公眾號      小麥苗的DBA寶典QQ群2     《DBA筆試面寶典》讀者群       小麥苗的微店

.............................................................................................................................................

MySQL EXPLAIN命令詳解學習(檢視執行計劃)
DBA筆試面試講解群
《DBA寶典》讀者群 歡迎與我聯絡



來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/26736162/viewspace-2125036/,如需轉載,請註明出處,否則將追究法律責任。

相關文章