本文來自我的github pages博客http://galengao.github.io/ 即www.gaohuirong.cn
摘要:
- 本篇是根據官網中的每個一點來翻譯、舉例、驗證的;英語不好,所以有些話語未必准確,請自行查看官網,若有些點下面沒有例子的是因為當時一下子沒有想出那么多來,如果大家有遇上好的例子,歡迎在下面留言我持續更新
- 查看執行計划的關鍵EXPLAIN
- 版本MYSQL5.6,用到的庫是官網例子sakila,自行下載導入
由於要把每個點都翻譯出來,還需要舉例,所以需要一定的時間,本人先把架構理出來,然后逐個點開始
官網地址:http://dev.mysql.com/doc/refman/5.6/en/explain-output.html
EXPLAIN語句返回MYSLQ的執行計划,通過他返回的信息,我們能了解到MYSQL優化器是如何執行SQL語句的,通過分析他能幫助你提供優化的思路。
語法
MYSQL 5.6.3以前只能EXPLAIN SELECT; MYSQL5.6.3以后就可以EXPLAIN SELECT,UPDATE,DELETE
- 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主要包含以下信息:
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)
查詢的類型,可以是下表的任何一種類型:
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) |
查詢類型例子:
1、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
2、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
3、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
4、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
5、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
6、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
試試
7、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
8、其它如物化視圖等查詢自己去造例子去
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輸出的類型列描述了表的連接方法。下面的列表介紹了連接類型,從最好的類型到最差的命令:
1、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
2、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;
3、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
4、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
5、 fulltext
使用FULLTEXT索引進行聯接。
6、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
此處按照官網的格式未測試出例子來,若有例子的請留言,我測試更新
7、index_merge
該聯接類型表示使用了索引合並優化方法。在這種情況下,key列包含了使用的索引的清單,key_len包含了使用的索引的最長的關鍵元素。
此處按照官網的格式未測試出例子來,若有例子的請留言,我測試更新
8、unique_subquery
unique_subquery是一個索引查找函數,可以完全替換子查詢,效率更高。
該類型替換了下面形式的IN子查詢的ref:
value IN (SELECT primary_key FROM single_table WHERE some_expr)
此處按照官網的格式未測試出例子來,若有例子的請留言,我測試更新
9、index_subquery
該聯接類型類似於unique_subquery。可以替換IN子查詢,但只適合下列形式的子查詢中的非唯一索引:
value IN (SELECT key_column FROM single_table WHERE some_expr)
此處按照官網的格式未測試出例子來,若有例子的請留言,我測試更新
10、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);
11、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