mysql有一个explain命令,对我们找到慢SQL后进行分析很有帮助,先通过一个简单的SQL语句来看看有explain哪些东西。通过执行explain select * from user;得到结果集是:
结果列 | 介绍 |
id | 执行语句的标记,记住两种情况即可: 1、id一样时,按顺序从上往下执行 2、id不一样,id大的先执行 |
select_type | 表示对应查询的语句类型 1、simple 简单查询 2、primary 复杂查询中最外层的select 3、subquery 包含在select中的子查询 4、derived 派生表 5、union union用法后的查询 |
table | 代表当前查询的表,如果有子查询,显示的是derived<id> |
type | 表示访问的类型,按性能来分,最优到最差的排序为:system>const>eq_ref>ref>range>index>ALL system,const:可以将部分查询转换成常量的查询,速度很快,用于primary key 或unique key的列和常数进行比较,最多只有一条数据。system是const的特例,代表表里只有一条数据时为system eq_ref:primary key 或unique key用于关联查询时,可能出现 ref: 使用普通索引时的类型(或者唯一索引的部分信息,比如使用联合主键时使用了头字段的情况) range:范围扫描时出现 index:扫描全索引获取结果(一般指扫描的二级索引) ALL:全表扫描 |
possible_keys | 表示可能会用到的索引信息 |
key | 显示使用哪个索引来进行查询 可能出现possible_keys有值但key为NULL的情况,这代表mysql内部认为使用索引可能效果不如全表查询 |
key_len | 表示mysql在索引里用到的字节数,一般根据索引字段的字节长度计算 |
ref | 显示表查找值所用到的列或者常量 |
rows | 执行sql估计要读取的行数 |
extra | 展示一些额外信息说明 1、Using index :表示使用覆盖索引,只使用索引就可拿到结构,不用回表 2、Using where:表示使用where语句处理结果,查询的列未被索引覆盖 3、Using index condition:使用了索引,还需要回表 4、Using temporary:需要使用一张临时表来处理查询,一般需要进行优化 5、Using filesort:排序中出现,且排序字段不在索引覆盖范围内 |
然后呢,我们使用一些SQL对这些字段属性做更进一步的认识。
CREATE TABLE `user` (
`id` int(11) NOT NULL,
`name` varchar(45) DEFAULT NULL,
`update_time` datetime NOT NULL ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `user` (`id`, `name`) VALUES (1,'a'), (2,'b'), (3,'c');
DROP TABLE IF EXISTS `job`;
CREATE TABLE `job` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(10) DEFAULT NULL,
`update_time` datetime NOT NULL ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `idx_name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `job` (`id`, `name`) VALUES (3,'job3'),(1,'film1'),(2,'film2');
DROP TABLE IF EXISTS `user_job`;
CREATE TABLE `user_job` (
`id` int(11) NOT NULL,
`job_id` int(11) NOT NULL,
`user_id` int(11) NOT NULL,
`remark` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `idx_user_job_id` (`job_id`,`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `user_job` (`id`, `job_id`, `user_id`) VALUES (1,1,1),(2,1,2),(3,2,1);
首先,创建几张表供我们使用,然后我们用一些执行语句来进行分析。
1、select_type及table分析语句
set session optimizer_switch='derived_merge=off';
explain select (select 1 from user where id = 1) from (select * from job where id = 1) der ;

从上面的例子,我们可以看到DERIVED是我们从job中查出数据的衍生表 ,SUBQUERY是一个子查询(select 1 from user where id = 1),PRIMARY是最外层的表信息,使用的表是派生表<derived3>2、type分析语句
explain select * from (select * from job where id = 1) der ;

这里我们关注类型,const类型的是id=1的派生表的查询情况,用于主键的查询且查询数据只有一条的情况,system是因为派生表里就一条数据(也就是刚刚查出来的id=1的那条),所以类型是system,可以看做const的特例。
explain select * from user_job left join job on user_job.job_id = job.id;

看eq_ref类型的数据,使用了主键索引进行关联查询出现,每次最多只返回一条符合的记录。
explain select * from job where name = 'job1';
explain select * from job where name like 'job%';


差不多的两条sql,一个是直接找一个值,一个是范围的查询,都使用了同一个索引idx_name,我们可以根据type区别出,ref的是普通索引的查询,range是范围查询
explain select id, name from job;

这里我们可以看到,查询job的id和name时,type是index而不是ALL,由于我们对name建立了索引,扫描二级索引就能得到想要的id和name的结果,所以就是index,如果没有这个索引或者再多查询一个字段,那就会变成ALL。比如:explain select id, name,create_time from job;

结果就变成了了ALL。
PS:一般来说,我们要尽量的去避免全局扫描,也就是type=ALL的情况。
3、extra分析
我们再来关注Extra列,上面的几个explain查询里面有些Extra是空的,有些里面有Using index。那这个SQL的结果来说(explain select id, name from job)

我们可以发现他们都使用了索引,并且不需要再去主键索引里找数据就能得到最终的结果。再看这句explain select * from user where name = 'a';
name没有放置索引,符合Using where的描述。
explain select id , name from user ORDER BY name;

explain select id , name from job ORDER BY name;

一个是Using filesort , 一个是Using index ,当我们有二级索引且运用上的时候,数据库会帮我们尽量的去使用索引,而没有二级的索引的使用的是外部排序。结合以上的分析来说,我们要优化一个SQL,那么第一个想到的就是让该走索引的都尽量走索引哦。