摘要: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 |