摘要:node
- 本篇是根據官網中的每一個一點來翻譯、舉例、驗證的;英語很差,因此有些話語未必準確,請自行查看官網,如有些點下面沒有例子的是由於當時一會兒沒有想出那麼多來,若是你們有趕上好的例子,歡迎在下面留言我持續更新
- 查看執行計劃的關鍵EXPLAIN
- 版本MYSQL5.6,用到的庫是官網例子sakila,自行下載導入
因爲要把每一個點都翻譯出來,還須要舉例,因此須要必定的時間,本人先把架構理出來,而後逐個點開始mysql
官網地址:http://dev.mysql.com/doc/refman/5.6/en/explain-output.htmlgit
EXPLAIN語句返回MYSLQ的執行計劃,經過他返回的信息,咱們能瞭解到MYSQL優化器是如何執行SQL語句的,經過分析他能幫助你提供優化的思路。github
語法
MYSQL 5.6.3之前只能EXPLAIN SELECT; MYSQL5.6.3之後就能夠EXPLAIN SELECT,UPDATE,DELETEsql
- EXPLAIN 語法例子:
mysql> explain select customer_id,a.store_id,first_name,last_name, b.manager_staff_id from customer a left join store b on a.store_id=b.store_id; +----+-------------+-------+--------+---------------+---------+---------+-------------------+------+-------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+--------+---------------+---------+---------+-------------------+------+-------+ | 1 | SIMPLE | a | ALL | NULL | NULL | NULL | NULL | 599 | NULL | | 1 | SIMPLE | b | eq_ref | PRIMARY | PRIMARY | 1 | sakila.a.store_id | 1 | NULL | +----+-------------+-------+--------+---------------+---------+---------+-------------------+------+-------+ 2 rows in set
- EXPLAIN還有一種語法,相似於desc
mysql> explain actor; +-------------+----------------------+------+-----+-------------------+-----------------------------+ | Field | Type | Null | Key | Default | Extra | +-------------+----------------------+------+-----+-------------------+-----------------------------+ | actor_id | smallint(5) unsigned | NO | PRI | NULL | auto_increment | | first_name | varchar(45) | NO | | NULL | | | last_name | varchar(45) | NO | MUL | NULL | | | last_update | timestamp | NO | | CURRENT_TIMESTAMP | on update CURRENT_TIMESTAMP | +-------------+----------------------+------+-----+-------------------+-----------------------------+ 4 rows in set
EXPLAIN的輸出
EXPLAIN主要包含如下信息:json
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 |
id (JSON name: select_id)
SQL查詢中的序列號。緩存
select_type (JSON name: none)
查詢的類型,能夠是下表的任何一種類型:ruby
select_type Value | JSON Name | Meaning |
---|---|---|
SIMPLE | None | 簡單查詢(不適用union和子查詢的) |
PRIMARY | None | 最外層的查詢 |
UNION | None | UNION中的第二個或者後面的SELECT語句 |
DEPENDENT UNION | dependent (true) | UNION中的第二個或者後面的SELECT語句,依賴於外部查詢 |
UNION RESULT | union_result | UNION結果 |
SUBQUERY | None | 子查詢中的第一個SELECT語句 |
DEPENDENT SUBQUERY | dependent (true) | 子查詢中的第一個SELECT語句,依賴於外部查詢 |
DERIVED | None | 派生表的SELECT(FROM子句的子查詢) |
MATERIALIZED | materialized_from_subquery | 物化子查詢 |
UNCACHEABLE SUBQUERY | cacheable (false) | 對於該結果不能被緩存,必須從新評估外部查詢的每一行子查詢 |
UNCACHEABLE UNION | cacheable (false) | UNION中的第二個或者後面的SELECT語句屬於不可緩存子查詢 (see UNCACHEABLE SUBQUERY) |
查詢類型例子:架構
一、SIMPLE 簡單查詢(不適用union和子查詢的)
mysql> explain select * from staff; +----+-------------+-------+------+---------------+------+---------+------+------+-------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+------+---------------+------+---------+------+------+-------+ | 1 | SIMPLE | staff | ALL | NULL | NULL | NULL | NULL | 2 | NULL | +----+-------------+-------+------+---------------+------+---------+------+------+-------+ 1 row in set
二、PRIMARY 最外層的查詢
mysql> explain select * from (select last_name,first_name from customer) a; +----+-------------+------------+------+---------------+------+---------+------+------+-------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+------------+------+---------------+------+---------+------+------+-------+ | 1 | PRIMARY | <derived2> | ALL | NULL | NULL | NULL | NULL | 599 | NULL | | 2 | DERIVED | customer | ALL | NULL | NULL | NULL | NULL | 599 | NULL | +----+-------------+------------+------+---------------+------+---------+------+------+-------+ 2 rows in set
三、UNION UNION中的第二個或者後面的SELECT語句
mysql> explain select first_name,last_name from customer a where customer_id=1 union select first_name,last_name from customer b where customer_id=2; +------+--------------+------------+-------+---------------+---------+---------+-------+------+-----------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +------+--------------+------------+-------+---------------+---------+---------+-------+------+-----------------+ | 1 | PRIMARY | a | const | PRIMARY | PRIMARY | 2 | const | 1 | NULL | | 2 | UNION | b | const | PRIMARY | PRIMARY | 2 | const | 1 | NULL | | NULL | UNION RESULT | <union1,2> | ALL | NULL | NULL | NULL | NULL | NULL | Using temporary | +------+--------------+------------+-------+---------------+---------+---------+-------+------+-----------------+ 3 rows in set
四、DEPENDENT UNION UNION中的第二個或者後面的SELECT語句,依賴於外部查詢
mysql> explain select * from customer where customer_id in(select customer_id from customer a where customer_id=1 union all select customer_id from customer b where customer_id=2); +------+--------------------+------------+-------+---------------+---------+---------+-------+------+-----------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +------+--------------------+------------+-------+---------------+---------+---------+-------+------+-----------------+ | 1 | PRIMARY | customer | ALL | NULL | NULL | NULL | NULL | 599 | Using where | | 2 | DEPENDENT SUBQUERY | a | const | PRIMARY | PRIMARY | 2 | const | 1 | Using index | | 3 | DEPENDENT UNION | b | const | PRIMARY | PRIMARY | 2 | const | 1 | Using index | | NULL | UNION RESULT | <union2,3> | ALL | NULL | NULL | NULL | NULL | NULL | Using temporary | +------+--------------------+------------+-------+---------------+---------+---------+-------+------+-----------------+ 4 rows in set
五、UNION RESULT UNION結果
mysql> explain select * from staff union select * from staff; +------+--------------+------------+------+---------------+------+---------+------+------+-----------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +------+--------------+------------+------+---------------+------+---------+------+------+-----------------+ | 1 | PRIMARY | staff | ALL | NULL | NULL | NULL | NULL | 2 | NULL | | 2 | UNION | staff | ALL | NULL | NULL | NULL | NULL | 2 | NULL | | NULL | UNION RESULT | <union1,2> | ALL | NULL | NULL | NULL | NULL | NULL | Using temporary | +------+--------------+------------+------+---------------+------+---------+------+------+-----------------+ 3 rows in set
六、SUBQUERY 子查詢中的第一個SELECT語句
mysql> explain select customer_id from customer where store_id = (select store_id from store where store_id=1); +----+-------------+----------+-------+-----------------+-----------------+---------+-------+------+--------------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+----------+-------+-----------------+-----------------+---------+-------+------+--------------------------+ | 1 | PRIMARY | customer | ref | idx_fk_store_id | idx_fk_store_id | 1 | const | 326 | Using where; Using index | | 2 | SUBQUERY | store | const | PRIMARY | PRIMARY | 1 | const | 1 | Using index | +----+-------------+----------+-------+-----------------+-----------------+---------+-------+------+--------------------------+ 2 rows in set
有興趣的能夠去把=號換成
in
試試
七、DERIVED 派生表的SELECT(FROM子句的子查詢)
mysql> explain select * from (select * from customer) a; +----+-------------+------------+------+---------------+------+---------+------+------+-------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+------------+------+---------------+------+---------+------+------+-------+ | 1 | PRIMARY | <derived2> | ALL | NULL | NULL | NULL | NULL | 599 | NULL | | 2 | DERIVED | customer | ALL | NULL | NULL | NULL | NULL | 599 | NULL | +----+-------------+------------+------+---------------+------+---------+------+------+-------+ 2 rows in set
八、其它如物化視圖等查詢本身去造例子去
table(JSON name: table_name)
顯示這一行的數據是關於哪張表的,也能夠是下列值之一:
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 the FROM clause.
subqueryN: The row refers to the result of a materialized subquery for the row with an id value of N.
partitions (JSON name: partitions)
分區中的記錄將被查詢相匹配。顯示此列僅在使用分區關鍵字。該值爲NULL對於非分區表。
type (JSON name: access_type)
EXPLAIN輸出的類型列描述了表的鏈接方法。下面的列表介紹了鏈接類型,從最好的類型到最差的命令:
一、system
這是const的一個特例聯接類型。表只有一行(=系統表)。
mysql> explain select * from (select * from customer where customer_id=1) a; +----+-------------+------------+--------+---------------+---------+---------+-------+------+-------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+------------+--------+---------------+---------+---------+-------+------+-------+ | 1 | PRIMARY | <derived2> | system | NULL | NULL | NULL | NULL | 1 | NULL | | 2 | DERIVED | customer | const | PRIMARY | PRIMARY | 2 | const | 1 | NULL | +----+-------------+------------+--------+---------------+---------+---------+-------+------+-------+ 2 rows in set
二、const
表最多有一個匹配行,它將在查詢開始時被讀取。由於僅有一行,在這行的列值可被優化器剩餘部分認爲是常數。const表很快,由於它們只讀取一次!
const用於用常數值比較PRIMARY KEY或UNIQUE索引的全部部分時。在下面的查詢中,tbl_name能夠用於const表:
SELECT * from tbl_name WHERE primary_key=1; SELECT * from tbl_name WHERE primary_key_part1=1和 primary_key_part2=2;
三、eq_ref
對於每一個來自於前面的表的行組合,從該表中讀取一行。這多是最好的聯接類型,除了const類型。它用在一個索引的全部部分被聯接使用而且索引是UNIQUE或PRIMARY KEY。
eq_ref能夠用於使用= 操做符比較的帶索引的列。比較值能夠爲常量或一個使用在該表前面所讀取的表的列的表達式。
在下面的例子中,MySQL可使用eq_ref聯接來處理ref_tables:
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使用的是非惟一索引或者普通索引。id是主鍵 mysql> explain select a.*,b.* from testa a,testb b where a.id=b.id ; +----+-------------+-------+--------+---------------+---------+---------+-------------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+--------+---------------+---------+---------+-------------+------+-------------+ | 1 | SIMPLE | b | ALL | NULL | NULL | NULL | NULL | 1 | Using where | | 1 | SIMPLE | a | eq_ref | PRIMARY | PRIMARY | 4 | sakila.b.id | 1 | NULL | +----+-------------+-------+--------+---------------+---------+---------+-------------+------+-------------+ 2 rows in set
四、ref
對於每一個來自於前面的表的行組合,全部有匹配索引值的行將從這張表中讀取。若是聯接只使用鍵的最左邊的前綴,或若是鍵不是UNIQUE或PRIMARY KEY(換句話說,若是聯接不能基於關鍵字選擇單個行的話),則使用ref。若是使用的鍵僅僅匹配少許行,該聯接類型是不錯的。
ref能夠用於使用=或<=>操做符的帶索引的列。
在下面的例子中,MySQL可使用ref聯接來處理ref_tables:
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;
# 使用非惟一性索引或者惟一索引的前綴掃描,返回匹配某個單獨值的記錄行。name有非惟一性索引 mysql> explain select * from testa where name='aaa'; +----+-------------+-------+------+---------------+----------+---------+-------+------+-----------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+------+---------------+----------+---------+-------+------+-----------------------+ | 1 | SIMPLE | testa | ref | idx_name | idx_name | 33 | const | 2 | Using index condition | +----+-------------+-------+------+---------------+----------+---------+-------+------+-----------------------+ 1 row in set mysql> explain select a.*,b.* from testa a,testb b where a.name=b.cname; +----+-------------+-------+------+---------------+----------+---------+----------------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+------+---------------+----------+---------+----------------+------+-------------+ | 1 | SIMPLE | b | ALL | NULL | NULL | NULL | NULL | 1 | Using where | | 1 | SIMPLE | a | ref | idx_name | idx_name | 33 | sakila.b.cname | 1 | NULL | +----+-------------+-------+------+---------------+----------+---------+----------------+------+-------------+ 2 rows in set
五、 fulltext
使用FULLTEXT索引進行聯接。
六、ref_or_null
該聯接類型如同ref,可是添加了MySQL能夠專門搜索包含NULL值的行。在解決子查詢中常用該聯接類型的優化。
在下面的例子中,MySQL可使用ref_or_null聯接來處理ref_tables:
SELECT * FROM ref_table WHERE key_column=expr OR key_column IS NULL;
mysql> explain select * from (select cusno from testa t1,testb t2 where t1.id=t2.id) t where cusno =2 or cusno is null; +----+-------------+------------+-------------+---------------+-------------+---------+--------------+------+--------------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+------------+-------------+---------------+-------------+---------+--------------+------+--------------------------+ | 1 | PRIMARY | <derived2> | ref_or_null | <auto_key0> | <auto_key0> | 5 | const | 2 | Using where; Using index | | 2 | DERIVED | t2 | index | PRIMARY | PRIMARY | 4 | NULL | 1 | Using index | | 2 | DERIVED | t1 | eq_ref | PRIMARY | PRIMARY | 4 | sakila.t2.id | 1 | NULL | +----+-------------+------------+-------------+---------------+-------------+---------+--------------+------+--------------------------+ 3 rows in set
此處按照官網的格式未測試出例子來,如有例子的請留言,我測試更新
七、index_merge
該聯接類型表示使用了索引合併優化方法。在這種狀況下,key列包含了使用的索引的清單,key_len包含了使用的索引的最長的關鍵元素。
此處按照官網的格式未測試出例子來,如有例子的請留言,我測試更新
八、unique_subquery
unique_subquery是一個索引查找函數,能夠徹底替換子查詢,效率更高。
該類型替換了下面形式的IN子查詢的ref:
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
只檢索給定範圍的行,使用一個索引來選擇行。key列顯示使用了哪一個索引。key_len包含所使用索引的最長關鍵元素。在該類型中ref列爲NULL。
當使用=、<>、>、>=、<、<=、IS NULL、<=>、BETWEEN或者IN操做符,用常量比較關鍵字列時,可使用range
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類型同樣,除了它是走索引樹掃描的,它有兩種方式:
若是該覆蓋索引能知足查詢的全部數據,那僅僅掃描這索引樹。在這種狀況下,Extra
列就會顯示用Using index
。通常僅僅用索引是掃描的比ALL掃描的要快,由於索引樹比表數據小不少。
全表掃描被用到從索引中去讀取數據, Extra
列就不會顯示用Using index
。
若是查詢僅僅是索引列,那MySQL會這個index
索引類型
mysql> alter table testa add primary key p_id(id); Query OK, 0 rows affected Records: 0 Duplicates: 0 Warnings: 0 mysql> create index idx_name on testa(name); Query OK, 0 rows affected Records: 0 Duplicates: 0 Warnings: 0 mysql> insert into testa values(2,2,'aaa'); Query OK, 1 row affected # 由於查詢的列name上建有索引,因此若是這樣type走的是index mysql> explain select name from testa; +----+-------------+-------+-------+---------------+----------+---------+------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+-------+---------------+----------+---------+------+------+-------------+ | 1 | SIMPLE | testa | index | NULL | idx_name | 33 | NULL | 2 | Using index | +----+-------------+-------+-------+---------------+----------+---------+------+------+-------------+ 1 row in set # 由於查詢的列cusno沒有建索引,或者查詢的列包含沒有索引的列,這樣查詢就會走ALL掃描,以下: mysql> explain select cusno from testa; +----+-------------+-------+------+---------------+------+---------+------+------+-------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+------+---------------+------+---------+------+------+-------+ | 1 | SIMPLE | testa | ALL | NULL | NULL | NULL | NULL | 2 | NULL | +----+-------------+-------+------+---------------+------+---------+------+------+-------+ 1 row in set # *包含有未見索引的列 mysql> explain select * from testa; +----+-------------+-------+------+---------------+------+---------+------+------+-------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+------+---------------+------+---------+------+------+-------+ | 1 | SIMPLE | testa | ALL | NULL | NULL | NULL | NULL | 2 | NULL | +----+-------------+-------+------+---------------+------+---------+------+------+-------+ 1 row in set
十二、all
對於每一個來自於先前的表的行組合,進行完整的表掃描。若是表是第一個沒標記const的表,這一般很差,而且一般在它狀況下不好。一般能夠增長更多的索引而不要使用ALL,使得行能基於前面的表中的常數值或列值被檢索出。
possible_keys (JSON name: possible_keys)
possible_keys列指出MySQL能使用哪一個索引在該表中找到行。而下面的key是MYSQL實際用到的索引,這意味着在possible_keys中的是計劃中的,而key是實際的,也就是計劃中有這個索引,實際執行時未必能用到。
若是該列是NULL,則沒有相關的索引。在這種狀況下,能夠經過檢查WHERE子句看是否它引用某些列或適合索引的列來提升你的查詢性能。若是是這樣,創造一個適當的索引而且再次用EXPLAIN檢查查詢。
例子參考下面
key (JSON name: key)
key列顯示MySQL實際決定使用的鍵(索引)。
若是沒有選擇索引,鍵是NULL。要想強制MySQL使用或忽視possible_keys列中的索引,在查詢中使用FORCE INDEX、USE INDEX或者IGNORE INDEX。
例子參考下面
key_len (JSON name: key_length)
key_len列顯示MySQL決定使用的鍵長度。若是KEY鍵是NULL,則長度爲NULL。
使用的索引的長度。在不損失精確性的狀況下,長度越短越好.
例子參考下面
ref (JSON name: ref)
ref列顯示使用哪一個列或常數與key一塊兒從表中選擇行。 它顯示的是列的名字(或單詞「const」),MySQL將根據這些列來選擇行。
例子參考下面
rows (JSON name: rows)
rows列顯示MySQL認爲它執行查詢時必須檢查的行數。
例子參考下面
filtered (JSON name: filtered)
若是你用EXPLAIN EXTENDED將會展現出這列filtered(MySQL5.7缺省就會輸出filtered),它指返回結果的行佔須要讀到的行(rows列的值)的百分比。按說filtered是個很是有用的值,由於對於join操做,前一個表的結果集大小直接影響了循環的次數。可是個人環境下測試的結果倒是,filtered的值一直是100%,也就是說失去了意義。
上面部分EXPLAIN展現的列的例子:
mysql> alter table testa add primary key p_id(id); Query OK, 0 rows affected Records: 0 Duplicates: 0 Warnings: 0 mysql> create index idx_name on testa(name); Query OK, 0 rows affected Records: 0 Duplicates: 0 Warnings: 0 mysql> insert into testa values(2,2,'aaa'); Query OK, 1 row affected # 下面possible_keys可能會用到的索引有主鍵和我建的索引,可是key實際用到的是主鍵,主鍵長度是4,ref用的列的名字(或單詞「const」,此處用的是常量const,速度快,rows掃描的只有1行 mysql> explain select cusno from testa where id=2 and name='aaa'; +----+-------------+-------+-------+------------------+---------+---------+-------+------+-------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+-------+------------------+---------+---------+-------+------+-------+ | 1 | SIMPLE | testa | const | PRIMARY,idx_name | PRIMARY | 4 | const | 1 | NULL | +----+-------------+-------+-------+------------------+---------+---------+-------+------+-------+ 1 row in set # 下面雖然name有索引,可是查詢的列cusno沒有索引,這時mysql計劃possible_keys有索引,但實際key未走索引,若果cusno換成有索引的列,參照下面。 mysql> explain select cusno from testa where name='aaa'; +----+-------------+-------+------+---------------+------+---------+------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+------+---------------+------+---------+------+------+-------------+ | 1 | SIMPLE | testa | ALL | idx_name | NULL | NULL | NULL | 1 | Using where | +----+-------------+-------+------+---------------+------+---------+------+------+-------------+ 1 row in set # id是有主鍵索引的,這時實際key已經走索引了,若果查詢列換成既有索引的列也有無索引的列,參照下面 mysql> explain select id from testa where name='aaa'; +----+-------------+-------+------+---------------+----------+---------+-------+------+--------------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+------+---------------+----------+---------+-------+------+--------------------------+ | 1 | SIMPLE | testa | ref |