Explain详解与索引优化最佳实践

目录

目标

  • 掌握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需要创建一张临时表来处理查询。出现这种情况一般是要进行优化的,首先是想到用索引来优化
相关推荐
计算机学姐2 小时前
基于SpringBoot的流浪动物救助收养系统
vue.js·spring boot·后端·mysql·java-ee·intellij-idea·mybatis
小李来了!2 小时前
Oracle、MySQL、SQL server介绍及有何区别
数据库·mysql·oracle·sqlserver
mcooiedo2 小时前
Mysql ONLY_FULL_GROUP_BY模式详解、group by非查询字段报错
数据库·mysql
执笔画情ora2 小时前
PG/mysql/oracle--- 长事务对后续事务影响分析
数据库·mysql·oracle
qq_283720052 小时前
nestjs实战(六):诺依Nest.js + MySQL 项目改造为兼容达梦8数据库详细教程
javascript·数据库·mysql·达梦·nest.js·诺依
看我干嘛!2 小时前
在Windows上安装MySQL的两种方法
数据库·mysql
半杯橙汁3 小时前
Navicat Trial 出现 ‌1251 报错问题解决
mysql
草莓熊Lotso3 小时前
MySQL 内置函数指南:日期、字符串、数学函数实战
android·java·linux·运维·数据库·c++·mysql
polaris06303 小时前
MySQL如何执行.sql 文件:详细教学指南
数据库·mysql