使用explain可以模拟优化器执行SQL语句,从而知道MySQL是如何处理你的SQL语句的。
CREATE TABLE `actor` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`film_id` int(11) DEFAULT NULL,
`name` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
`update_time` datetime DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `idx_film_id` (`film_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8
insert into `actor`(`id`,`film_id`,`name`,`update_time`) values (1,1,'actor1','2021-05-04 17:14:44'),(2,2,'actor2','2021-05-04 17:17:18'),(3,3,'actor3','2021-05-04 17:17:18'),(4,4,'actor2','2021-05-04 17:17:18');
CREATE TABLE `film` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(20) DEFAULT NULL,
`remark` varchar(100) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `idx_name` (`name`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8
insert into `film`(`id`,`name`,`remark`) values (1,'film1',NULL),(2,'film2',NULL),(3,'film3',NULL);
EXPLAIN SELECT NAME FROM actor WHERE film_id = (SELECT id FROM film WHERE NAME = 'film2')
先执行后面的子查询(id列越大执行优先级越高,越先执行)
- select_type 表示对应行是简单还是复杂的查询
- 简单查询,查询不包含子查询和union。
- 复杂查询(包含子查询或union)中最外层的 select,就会被标记primary;如下查询actor时,就是primary
- 包含在 select或where 中的子查询(不在 from 子句中);像上图子查询的film表
- 包含在 from 子句中的子查询。MySQL会将结果存放在一个临时表中,也称为派生表(derived的英文含义);见下方union result
- 第二个select出现在union之后,则被标记union
- 从 union 临时表检索结果的 select
- mysql能够在优化阶段分解查询语句,在执行阶段用不着再访问表或索引。
- 例如:在索引列中选取最小值,可以单独查找索引来完成,不需要在执行时访问表
- const表示通过索引一次就能找到,用于 primary key 或 unique key 的所有列与常数比较时
- system是const的特例,表里只有一条数据,匹配时为system,可忽略
- 唯一索引扫描,简单的 select 查询不会出现这种 type。
- primary key 或 unique key 索引键作为关联条件 ,表中只有一条记录与之匹配。
- 不使用唯一索引,而是使用普通索引或者唯一性索引的部分前缀,索引要和某个值相比较,可能会找到多个符合条件的行。
film的name是普通索引
- 范围扫描通常出现在in(), between ,> ,<, >= 等操作中。使用一个索引来检索给定范围的行。
- 扫描全表索引,这通常比ALL快一些。(index是从索引中读取的,而all是从硬盘中读取)
- film的name是普通索引,id是主键索引,通过索引就能获取这两个字段值
- 如果select * ,那么其他字段不能从索引获取,就需要全表扫描
- 即全表扫描,意味着mysql需要从头到尾去查找所需要的行。通常情况下这需要增加索引来进行优化了
理论上用到的索引有两个,但实际用主键索引就够了
actor是有主键索引的,这里union就失效了,可以看到possible_keys和key列都为null
- 计算规则:
字符串 char(n):n字节长度
varchar(n):如果是utf-8,则长度 3n + 2 +1,2字节存储字符串长度,允许为null占1字节
tinyint:1字节
smallint:2字节
int:4字节
bigint:8字节
date:3字节
timestamp:4字节
datetime:8字节
允许为null占1字节
主键id不为null,为int类型,所以索引使用到的字节数4
- name为varchar(20),所以最大60,还有2字节存储字符串长度,此外允许为null占1字节
- 所以idx_name索引使用到的字节数为60+2+1=63
这一列是mysql估计要读取并检测的行数,注意这个不是结果集里的行数。
查询的列被索引覆盖,是性能高的表现。一般是使用了覆盖索引(索引包含了所有查询的字段)。
film的name是普通索引,id是主键索引,通过索引就能获取这两个字段值
- 表示mysql服务器将在存储引擎检索行后再进行过滤。
- 许多where条件里涉及索引中的列,当(并且如果)它读取索引时,就能被存储引擎检验
- 因此不是所有带where字句的查询都会显示"Using where"。
9.3. NULL
查询的列未被索引覆盖,并必须通过“回表”来查找,不是纯粹地用到了索引,也不是完全没用到索引
用到了主键索引,但是其他字段不能通过索引获取,需要回表去查找
需要创建一张临时表来处理查询。常见于排序、去重、分组
MySQL中无法利用索引完成的排序操作称为“文件排序”