MySQL - EXPLAIN詳解

平常工做中,咱們有時會經過日誌記錄下耗時較長的SQL語句,可是光找出這些SQL語句並不意味着完事了,經常須要藉助 EXPLAIN來查看SQL語句的執行計劃,查看SQL語句是否用上了索引,是否進行了全表掃描,這均可以經過 EXPLAIN命令獲得。

<!-- more -->html

概述

EXPLAIN:SELECT語句中使用到的每一個表返回一條信息。它按照MySQL在處理語句時讀取它們的順序列出這些表。MySQL使用循環嵌套算法解析全部鏈接。意味着MySQL從第一個表中讀取一行,而後在第二個表,第三個表中找到匹配的行,等等。mysql

QEP: SQL語句的查詢執行計劃算法

注意:

在之前版本的MySQL中,使用EXPLAIN PARTITIONSEXPLAIN EXTENDED 來生成分區和擴展信息 。目前爲止這些語法仍然是向後兼容的,但將來MySQL會將它們排除出EXPLAIN語法,由於如今EXPLAIN默認就會輸出分區擴展的相關信息。因此PARTITIONSEXTENDED關鍵字是多餘的,不推薦使用,且在使用時會提示警告。sql

EXPLAIN 輸出

本部分着重描述EXPLAIN生成的結果。更多關於 typeExtra 列的信息會在下文一一的介紹 。數據庫

mysql> EXPLAIN SELECT * FROM customer;
+----+-------------+----------+------+---------------+------+---------+------+--------+-------+
| id | select_type | table    | type | possible_keys | key  | key_len | ref  | rows   | Extra |
+----+-------------+----------+------+---------------+------+---------+------+--------+-------+
|  1 | SIMPLE      | customer | ALL  | NULL          | NULL | NULL    | NULL | 936161 |       |
+----+-------------+----------+------+---------------+------+---------+------+--------+-------+
1 row in set

查看結果

id(JSON名: select_id)

SELECT 標識符,SQL執行的順序的標識,SQL從大到小的執行緩存

  • id相同時,執行順序由上至下
  • 若是是子查詢,id的序號會遞增,id值越大優先級越高,越先被執行
  • 若是id相同,則認爲是一組,從上往下順序執行;在全部組中,id值越大,優先級越高,越先執行
select_type(JSON名:無)

SELECT 類型,能夠是下表顯示中的任何類型。服務器

查看結果

table(JSON名: table_name)
mysql> EXPLAIN SELECT t1.* FROM (SELECT mobile FROM customer GROUP BY mobile) t1;
+----+-------------+------------+------+---------------+------+---------+------+--------+---------------------------------+
| id | select_type | table      | type | possible_keys | key  | key_len | ref  | rows   | Extra                           |
+----+-------------+------------+------+---------------+------+---------+------+--------+---------------------------------+
|  1 | PRIMARY     | <derived2> | ALL  | NULL          | NULL | NULL    | NULL | 936161 |                                 |
|  2 | DERIVED     | customer   | ALL  | NULL          | NULL | NULL    | NULL | 936161 | Using temporary; Using filesort |
+----+-------------+------------+------+---------------+------+---------+------+--------+---------------------------------+
2 rows in set
type(JSON名: access_type)

查看結果

partitions(JSON名: partitions)

記錄與查詢匹配的分區。值爲NULL表示爲非分區表(5.7纔有)微信

possible_keys(JSON名: possible_keys)

表示MySQL查找表中的行時可選擇的索引。請注意,此列徹底獨立於EXPLAIN輸出中顯示的順序。 這意味着在possible_keys中的某些鍵實際上不能按生成的表順序使用。網絡

若是該列是NULL,則表明沒有相關的索引。在這種狀況下,能夠經過檢查WHERE子句看它是否引用了某些列或適合索引的列來提升查詢性能。若是是這樣,那麼就須要創造一個適當的索引,並再次用EXPLAIN檢查架構

key(JSON名:key)

顯示MySQL實際決定使用的鍵(索引),若是MySQL決定使用其中一個possible_keys 索引來查找行,則該索引被列爲關鍵值。

若是沒有選擇索引,鍵是NULL。要想強制MySQL使用或忽視possible_keys列中的索引,在查詢中使用FORCE INDEX、USE INDEX或者IGNORE INDEX

對於InnoDB而言,即使是查詢也選擇主鍵索引,輔助索引(secondary index)可能會覆蓋所選列,由於InnoDB將主鍵值存儲在每一個輔助索引中。若是key爲NULL,則表明MySQL未發現可用於提升效率的索引。

對於MyISAM的表,運行 ANALYZE TABLE 有助於優化器選擇更好的索引。myisamchk --analyze 也是如此。

key_len(JSON名: key_length)

顯示MySQL使用索引鍵的長度。若是key是NULL,則key_len爲NULL。使用的索引的長度。在不損失精確性的狀況下,長度越短越好

ref(JSON名:ref)

被用來標識那些用來進行索引比較的列或者常量

rows (JSON名 : rows)

表示MySQL根據表統計信息及索引選用狀況,估算的找到所需的記錄所須要讀取的行數

filtered(JSON名: filtered)

給出了一個百分比的值,這個百分比值和 rows 列的值一塊兒使用。(5.7纔有)

Extra (JSON名稱:無)

MySQL的附加信息,提供了與操做有關聯的信息

EXPLAIN JOIN Types 詳解

下面將描述從最佳類型到最差類型的鏈接類型

system

該表只有一行數據。這是const鏈接類型的特例

const

查詢開始時讀取,最多匹配出一行記錄。因爲只有一行,所以該行中列的值會被優化器視爲常量const速度很是快,由於它們只讀一次。

示例代碼:

SELECT * FROM tbl_name WHERE primary_key = 1;

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

效率僅次於systemconst,能夠用於=運算符進行比較的索引列,比較值能夠是一個常量,也能夠是一個表達式。

示例代碼:

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

ref能夠用於使用 =、or <=> 運算符進行比較的索引列 。

示例代碼:

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

查詢時使用 FULLTEXT 索引。

ref_or_null

該類型與 ref 相似,不一樣的是,它還對包含NULL的行進行額外的搜索。常做用在解析子查詢中。

示例代碼:

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

此鏈接類型表示使用的是索引合併優化

示例代碼:

SELECT * FROM tbl_name WHERE key1 = 10 OR key2 = 20;

SELECT * FROM tbl_name
  WHERE (key1 = 10 OR key2 = 20) AND non_key = 30;

SELECT * FROM t1, t2
  WHERE (t1.key1 IN (1,2) OR t1.key2 LIKE 'value%')
  AND t2.key1 = t1.some_col;

SELECT * FROM t1, t2
  WHERE t1.key1 = 1
  AND (t2.key1 = t1.some_col OR t2.key2 = t1.some_col2);

缺陷:

  • 全文索引不適用於合併。
  • MySQL不會選擇包含多層 AND/ OR 嵌套的複雜子句(修復方式以下)。
(x AND y) OR z => (x OR z) AND (y OR z)
(x OR y) AND z => (x AND z) OR (y AND z)
unique_subquery

只是一個索引查找函數,能夠徹底替代子查詢以提升效率。

示例代碼:

value IN (SELECT primary_key FROM single_table WHERE some_expr)
index_subquery

這種鏈接類型與 unique_subquery相似,取代了IN子查詢。

示例代碼:

value IN (SELECT key_column FROM single_table WHERE some_expr)
range

只檢索在給定範圍內的行。輸出行中的列指出使用的具體索引。這個類型的ref列是NULL。

示例代碼:

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

索引鏈接類型與ALL相同,只是索引樹被掃描了。當查詢只使用到單個索引的部分列時,MySQL就會使用這種Join Types。主要體如今兩個方面:

  • 若是查詢索引被覆蓋了,且知足表中所需的全部數據,這時只掃描索引樹。它比ALL掃描的要快,由於索引樹比表數據小不少。Extra 列中會給出 Using index字眼的信息。
  • 使用索引讀取數據,以索引順序查找數據行,進行完整的表掃描。使用的索引信息不會出如今Extra列中。
ALL

全表掃描,性能最糟,能夠經過添加索引來避免。

EXPLAIN Extra 詳解

一下列表表示可能出如今Extra中的值。若是要儘量快的查詢,那麼瞭解下面內容是不錯的選擇。

const row not found(JSON屬性: const_row_not_found)

對空表作相似 SELECT ... FROM tbl_name 的查詢操做

Deleting all rows(JSON屬性: message)

使用DELETE時,某些存儲引擎(MyISAM)支持的一些簡單、快速的處理方法。若是引擎使用到此類優化就會顯示該內容

Distinct(JSON屬性: distinct)

去重搜索是會顯示出該內容

FirstMatch(tbl_name) (JSON屬性:first_match)

表示 tbl_name 使用的半鏈接的FirstMatch鏈接策略。

Full scan on NULL key (JSON property: message)

當查詢優化器不能使用索引查詢時,那麼查詢優化後執行回退策略。

Impossible HAVING(JSON屬性: message)

HAVING條件過濾沒有效果,或者是始終選不出任何列(理解爲返回已有查詢的結果集)。

Impossible WHERE (JSON屬性:message)

WHERE條件過濾沒有效果,或者是始終選不出任何列(理解爲最終是全表掃描)。

Impossible WHERE noticed after reading const tables (JSON屬性:message)

查詢了全部const(常量表和系統表),但發現WHERE查詢條件不起做用。

LooseScan(m..n) (JSON屬性:message)

使用半鏈接LooseScan策略。 m 和 n是索引部分的數量

No matching min/max row(JSON屬性: message)

沒有行知足查詢的條件,如 SELECT MIN(...) FROM ... WHERE condition

No matching row in const table(JSON屬性:message)

對於鏈接查詢,列未知足惟一索引的條件或表爲空。

No matching rows after partition pruning(JSON屬性: message)

對於DELETE 或 UPDATE,優化器在分區以後,未發現任何要刪除或更新的內容。相似查詢 Impossible WHERE

No tables used(JSON屬性: message)

查詢沒有FROM子句,或者有一個 FROM DUAL子句。

Not exists(JSON屬性: message)

MySQL可以對LEFT JOIN查詢進行優化,而且在查找到符合LEFT JOIN條件的行後,則再也不查找更多的行。

示例代碼:

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

假定t2.id被定義爲 NOT NULL。在這種狀況下,MySQL 使用t1.id列的值查找t2表中的行 。若是找到匹配的行,且知道 t2.id不多是 NULL,那麼將不在繼續查找t2表中剩餘id相同的行。換句話說,對於每一行,MySQL只須要進行一次查詢,而無論有多少行夠與其匹能對應

Plan isn't ready yet (JSON屬性:無)

這個值的產生在EXPLAIN FOR CONNECTION,當優化器不能按照被命名的查詢鏈接來建立一個執行器計劃時就會出現Plan isn't ready yet。若是執行計劃的輸出包含了多行,全部行均可以有該值,則取決於優化器來決定完整的執行計劃。

Range checked for each record (index map: N)(JSON屬性: message)

MySQL沒有發現可使用的教好的索引,可是發現一些索引也許能使用在已有表的列值上。對於已有表格數據的每一行比較,檢查是否可使用rangeindex_merge 方法來檢索行。雖然不是最快的,但也比徹底不用索引要快的多。

Scanned N databases(JSON屬性: message)

表示處理INFORMATION_SCHEMA表查詢時服務器執行的掃描次數。關於N的值能夠是0,1,或者是all.

詳情參考:https://dev.mysql.com/doc/refman/5.7/en/information-schema-optimization.html

Start temporary,End temporary(JSON屬性: message)

說明在半鏈接複製清除策略中使用了臨時表

unique row not found(JSON屬性: message)

對於相似於SELECT ... FROM tbl_name的查詢,表中找不到知足條件惟一索引或主鍵索引的列。

Using filesort(JSON屬性: using_filesort)

MySQL必須作一個額外的傳遞才能找出按排序的順序檢索數據。經過鏈接類型存儲的排序關鍵字和WHERE查詢條件等一塊兒肯定的。而後對鍵進行排序,並按排序順序檢索行。

Using index(JSON屬性: using_index)

只需經過索引樹就能夠從表中獲取列的信息,無需額外去讀取真實的行數據。若是查詢使用的列值僅僅是一個簡單索引的部分值,則會使用這種策略來優化查詢。對於innoDB數據庫中的表有一個自定義的聚簇索引,該索引可以起做用,即便是Using index並無出如今Extra列中。這種狀況下的type字段爲index而且key字段的值爲PRIMARY。

Using index condition(JSON屬性: using_index_condition)

表的讀取首先經過讀入索引值來判斷是否須要全表掃描。在這種方式中,若是有須要的話。索引信息將被用來服務(壓入)全表掃描的。

Using index for group-by(JSON屬性:using_index_for_group_by)

相似於Using index的表查詢方法,指MySQL發現索引可以被用來查找 group byDISTINCT的列,而不須要任何真實的表查詢。另外,索引使得每一個分組查找都更有效,只有少許的索引值須要讀取。

Using join buffer (Block Nested Loop), Using join buffer (Batched Key Access) (JSON屬性:using_join_buffer)

從已有鏈接中找被讀入緩存的數據,而且經過緩存來完成與當前表的鏈接。(Block Nested Loop)說明使用了塊循環算法,(Batched key Access)說明使用了批量接入關鍵字算法。也就是說,在EXPLAIN輸出記錄中,從已經查找過的表中將輸出的列緩存下來,並在須要時批量的找出與當前數據對比,這時就會出現Using join buffer

在JSON格式的輸出中,using_join_buffer的值要麼是Block Nested Loop,要麼是Batched Key Access.

Using MRR(JSON屬性: message)

使用多範圍讀取的優化策略來讀取表中的數據。

示例代碼:(假設有一個索引: (key_part1, key_part2))

SELECT * FROM t
  WHERE key_part1 >= 1000 AND key_part1 < 2000
  AND key_part2 = 10000;

對於MRR,經過配置系統變量read_rnd_buffer_size來做爲它的緩衝區,並經過它來肯定每次最大處理字節數。

Using sort_union(...),Using union(...),Using intersect(...)(JSON屬性: message)

表示在index_merge的鏈接類型中索引合併是怎麼樣完成的,及使用了怎樣特別的算法。

Using temporary(JSON屬性: using_temporary_table)

爲了執行查詢,MySQL須要建立一個臨時表來存儲已有的結果。若是發現查詢中group byorder by是不一樣的列,則會有該類型產生。

Using where(JSON屬性: attached_condition)

WHERE條件用於賽選出與下一個表匹配的數據而後返回給客戶端。除非故意作的全表掃描,不然鏈接類型是ALL或者是index,且在Extra列的值中沒有Using Where,則該查詢多是有問題的。

Using where with pushed condition(JSON屬性:message)

該內容只適用在NDB的表中。意味着NDB集羣中正在使用「pushed down」優化策略,保證了經過網絡只發送有用的數據,且比未優化的狀況下提升了5-10倍的速度。

Zero limit(JSON屬性: message)

查詢條件中有LIMIT 0 而且沒有任何能夠選擇的記錄。

說點什麼

關注微信公衆號:battcn 後臺回覆 mysql 便可得到 《打造扛得住的MySQL數據庫架構》

  • 我的QQ:1837307557
  • battcn開源羣(適合新手):391619659
相關文章
相關標籤/搜索