平常工做中,咱們有時會經過日誌記錄下耗時較長的SQL語句,可是光找出這些SQL語句並不意味着完事了,經常須要藉助EXPLAIN
來查看SQL語句的執行計劃,查看SQL語句是否用上了索引,是否進行了全表掃描,這均可以經過EXPLAIN
命令獲得。
<!-- more -->html
EXPLAIN: 爲SELECT
語句中使用到的每一個表返回一條信息。它按照MySQL在處理語句時讀取它們的順序列出這些表。MySQL使用循環嵌套算法解析全部鏈接。意味着MySQL從第一個表中讀取一行,而後在第二個表,第三個表中找到匹配的行,等等。mysql
QEP: SQL語句的查詢執行計劃算法
注意:
在之前版本的MySQL中,使用EXPLAIN PARTITIONS
與 EXPLAIN EXTENDED
來生成分區和擴展信息 。目前爲止這些語法仍然是向後兼容的,但將來MySQL
會將它們排除出EXPLAIN
語法,由於如今EXPLAIN
默認就會輸出分區和擴展的相關信息。因此PARTITIONS
與 EXTENDED
關鍵字是多餘的,不推薦使用,且在使用時會提示警告。sql
本部分着重描述EXPLAIN
生成的結果。更多關於 type
和 Extra
列的信息會在下文一一的介紹 。數據庫
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從大到小的執行緩存
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的附加信息,提供了與操做有關聯的信息
下面將描述從最佳類型到最差類型的鏈接類型
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
效率僅次於system
和 const
,能夠用於=運算符
進行比較的索引列,比較值能夠是一個常量,也能夠是一個表達式。
示例代碼:
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);
缺陷:
(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
。主要體如今兩個方面:
Using index
字眼的信息。Extra
列中。ALL
全表掃描,性能最糟,能夠經過添加索引來避免。
一下列表表示可能出如今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沒有發現可使用的教好的索引,可是發現一些索引也許能使用在已有表的列值上。對於已有表格數據的每一行比較,檢查是否可使用range
或 index_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 by
或 DISTINCT
的列,而不須要任何真實的表查詢。另外,索引使得每一個分組查找都更有效,只有少許的索引值須要讀取。
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 by
和order 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數據庫架構》