目录
-
- 目标
- EXPLAIN列详解
-
- id
- select_type
- table
- partitions
- type
- possible_keys列
- key列
- key_len列
- ref列
- rows列
- [filtered 列](#filtered 列)
- extra列
目标
- 掌握EXPLAIN关键字用法,能读懂EXPLAIN执行计划含义,通过执行计划分析 SQL 查询的性能瓶颈。
- 理解EXPLAIN两个变种extended和partitions的功能与使用场景
- 掌握 MySQL 索引使用的最佳实践,能识别导致索引失效的常见场景,并能针对性优化 SQL 语句
- 能结合EXPLAIN执行计划和索引优化规则,独立完成慢 SQL 的分析与优化
EXPLAIN列详解
sql
示例表:
DROP TABLE IF EXISTS `actor`;
CREATE TABLE `actor` (
`id` int(11) NOT NULL,
`name` varchar(45) DEFAULT NULL,
`update_time` datetime DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `actor` (`id`, `name`, `update_time`) VALUES (1,'a','2017-12-22 15:27:18'), (2,'b','2017-12-22 15:27:18'), (3,'c','2017-12-22 15:27:18');
DROP TABLE IF EXISTS `film`;
CREATE TABLE `film` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(10) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `idx_name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `film` (`id`, `name`) VALUES (3,'film0'),(1,'film1'),(2,'film2');
DROP TABLE IF EXISTS `film_actor`;
CREATE TABLE `film_actor` (
`id` int(11) NOT NULL,
`film_id` int(11) NOT NULL,
`actor_id` int(11) NOT NULL,
`remark` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `idx_film_actor_id` (`film_id`,`actor_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `film_actor` (`id`, `film_id`, `actor_id`) VALUES (1,1,1),(2,1,2),(3,2,1);
通过EXPLAIN关键字可以清楚看出SQL的分析情况
sql
explain select * from actor;

id
SQL执行顺序,id越大先执行,id相同从上往下执行,id为NULL最后执行。
select_type
对应行是简单还是复杂查询
- simple:简单查询,不包含子查询和union
- primary:复杂查询中最外层的 select
- subquery:包含在 select 后的子查询
- derived:包含在 from 子句中的子查询。MySQL会将结果存放在一个临时表中
sql
set session optimizer_switch='derived_merge=off'; #关闭mysql5.7新特性对衍生表的合并优化
explain select (select 1 from actor where id = 1) from (select * from film where id = 1) der;

table
正在访问的表名
partitions
分区表,几乎不用
type
此列表示MYSQL如何去查询表中的行,从最优到最差分别为:system > const > eq_ref > ref > range > index > ALL,一般要优化到range,最好是ref
NULL:此情况出现较少,在分析阶段就能分解查询语句,比如直接在索引上求最小值
const, system:最多一行数据,像是查常量一样
eq_ref:primary key 或 unique key 索引的所有部分被连接使用 ,最多只会返回一条符合条件的记录
ref:相比 eq_ref,不使用唯一索引,而是使用普通索引或者唯一性索引的部分前缀
range:范围扫描通常出现在 in(), between ,> ,<, >= 等操作中。使用一个索引来检索给定范围的行
index:扫描全索引就能拿到结果,一般是扫描某个二级索引,这种扫描不会从索引树根节点开始快速查找,而是直接对二级索引的叶子节点遍历和扫描,速度还是比较慢的,这种查询一般为使用覆盖索引,二级索引一般比较小,所以这种通常比ALL快一些。
ALL:即全表扫描,扫描你的聚簇索引的所有叶子节点。通常情况下这需要增加索引来进行优化了。
possible_keys列
这一列显示查询可能使用哪些索引来查找。
explain 时可能出现 possible_keys 有列,而 key 显示 NULL 的情况,这种情况是因为表中数据不多,mysql认为索引对此查询帮助不大,选择了全表查询。
key列
这一列显示mysql实际采用哪个索引来优化对该表的访问。
key_len列
显示了mysql在索引里使用的字节数,通过这个值可以算出具体使用了索引中的哪些列
ref列
显示了在key列记录的索引中,表查找值所用到的列或常量,常见的有:const(常量),字段名(例:film.id)
rows列
mysql估计要读取并检测的行数,注意这个不是结果集里的行数
filtered 列
百分比的值,rows * filtered/100 可以估算出将要和 explain 中前一个表进行连接的行数
extra列
- Using index:使用覆盖索引
- Using where:使用 where 语句来处理结果,并且查询的列未被索引覆盖
- Using index condition:查询的列不完全被索引覆盖,where条件中是一个前导列的范围
- Using temporary:mysql需要创建一张临时表来处理查询。出现这种情况一般是要进行优化的,首先是想到用索引来优化