5-02 Explain详解与索引最佳实践
2022-12-09 23:43:39 0 举报
02 Explain详解与索引最佳实践
作者其他创作
大纲/内容
Explain详解
Explain工具介绍
1. 使用Explain关键字可以模拟优化器执行SQL语句,分析你的查询语句或是结构的性能瓶颈
2. 在select 语句之前添加explain关键字,MySQL 会在查询上设置一个标记,执行查询会返回执行计划的信息,并不是执行这条SQL
Explain分析示例
应用示例:
示例表:(Explain两个变种、Explain中的列中的示例都会用到)
在查询中的每个表会输出一行,如果有两个表通过 join 连接查询,那么会输出两行
Explain两个变种
1、explain extended
在explain的基础上额外提供一些查询优化的信息
相比 explain 多了个 filtered 字段:
是一个半分比的值
rows*filtered/100 可以估算出将要和 explain 中前一个表进行连接的行数
如下图所示:
2、explain partitions
相比 explain 多了个 partitions 字段
如果查询是基于分区表的话,会显示查询将访问的分区
如下图所示:
3、紧随其后的 show warnings
紧随其后通过 show warnings 命令可以得到优化后的查询语句,从而看出优化器优化了什么
如下图示例1:
如下图示例2:
Explain中的列
id列
id列的编号是select的序列号,有几个select就有几个id
id的编号是按select出现的顺序增长的
id编号越大执行优先级越高
id相同时,则从上往下执行
id为NULL时,则最后执行
select_type列
表示对应行是简单查询?还是复杂查询?
1)simple:简单查询。查询不包含子查询和union
如下图示例:
2)primary:复杂查询中最外层的select查询
如下图示例:
3)subquery:包含在select中的子查询(不在from子句中)
如下图示例:
4)derived:包含在from子句中的子查询。
MySQL会将包含在from子句中的子查询的结果存放在一个临时表中,也称为派生表(derived的英文含义)
如下图示例1:关闭5.7新特性-对衍生表的合并优化
如下图示例2:
如下图示例3:开启5.7新特性-对衍生表的合并优化
5)union:在union中的第二个和随后的select
如下图示例:
table列
这一列表示 explain 的一行正在访问哪个表
如下图示例:
当 from 子句中有子查询时,table 列是 <derivenN> 格式,表示当前查询依赖 id=N 的那行的查询,需要先执行 id=N 的查询
如下图示例:
当 union 时,UNION RESULT 行的 table 列的值为<union 1,2>,1和2表示参与union的 select 行id
如下图示例:
type列
这列表示关联类型或访问类型
即:MySQL决定如何查找表中的行,查找数据行记录的大概范围
依次从最优到最差分别为:
NULL > system > const > eq_ref >ref > range > index > ALL
一般得保证查询达到 range 级别,最好达到 ref 级别
1)NULL:MySQL能够在优化阶段分解查询语句,在执行阶段用不着再访问表或索引。
如下图所示:在索引列中选取最小值,可以单独查找索引来完成,不需要在执行时访问表。
2)system,const:
MySQL能对查询的某部分进行优化并将其转化成一个常量(可以看show warnings 的结果)
const 用于primary key 或 unique key 的所有列与常数比较时,所以表最多匹配到一行,读取1次,速度比较快
system 是 const 的特例,表里只有一条元组匹配时为 system
如下图所示:
3)eq_ref:
primary key 或 unique key 索引的所有部分被连接使用 ,最多只会返回一条符合条件的记录
这可能是在const 之外最好的关联类型了,简单的 select 查询不会出现这种 type
如下图所示:
4)ref:
相比 eq_ref,不使用唯一索引,而是使用普通索引或者唯一性索引的部分前缀
索引要和某个值相比较,可能会找到多个符合条件的行
1. 简单 select 查询,name是普通索引(非唯一索引)
如下图所示:
2. 关联表查询,idx_film_actor_id 是film_id 和 actor_id的联合索引,这里使用到了film_actor的左边前缀film_id部分
如下图所示:
5)range:
范围扫描通常出现在 in(), between ,> ,<, >= 等操作中
使用一个索引来检索给定范围的行
如下图所示:
6)index:
扫描全索引就能拿到结果
一般是扫描某个二级索引
这种扫描不会从索引树根节点开始快速查找
而是直接对二级索引的叶子节点遍历和扫描,速度还是比较慢的
这种查询一般为使用覆盖索引
二级索引一般比较小,所以这种通常比ALL快一些
如下图所示:
7)ALL:
即全表扫描,扫描你的聚簇索引(主键索引)的所有叶子节点
通常情况下这需要增加二级索引来进行优化了
如下图所示:
possible_keys列
表示这一列显示查询可能使用哪些索引来查找
explain 时可能出现 possible_keys 有值,而 key 显示 NULL 的情况
这种情况是因为表中数据不多,MySQL认为索引对此查询帮助不大,选择了全表查询
如果该列是NULL,则没有相关的索引
在这种情况下,可以通过检查 where 子句看是否可以创造一个适当的索引来提高查询性能,然后用 explain 查看效果
key列
这一列显示 MySQL 实际采用哪个索引来优化对该表的访问
如果没有使用索引,则该列是 NULL
如果想强制 MySQL 使用或忽视 possible_keys 列中的索引引,在查询中使用 forceindex、ignore index。
key_len列
这一列显示了mysql在索引里使用的字节数
通过这个值可以算出具体使用了索引中的哪些列
举例来说:
film_actor 表的联合索引 idx_film_actor_id 由 film_id 和 actor_id 两个int列组成,并且每个int是4字节
结果中的 key_len=4 可推断出查询使用了第一个列:film_id 列来执行索引查找
如下图所示:
key_len计算规则:
字符串
char(n):如果存汉字长度就是 3n 字节
varchar(n):如果存汉字则长度是 3n + 2 字节,加的2字节用来存储字符串长度,因为varchar是变长字符串
数值类型
tinyint:1字节
smallint:2字节
int:4字节
bigint:8字节
时间类型
date:3字节
timestamp:4字节
datetime:8字节
如果字段允许为 NULL,需要1字节记录是否为 NULL
索引最大长度是768字节,当字符串过长时,MySQL会做一个类似左前缀索引的处理,将前半部分的字符提取出来做索引
ref列
这一列显示了在key列使用到的索引中,表查找值所用到的列或常量
常见的有:const(常量),字段名(例:film.id)
rows列
这一列是MySQL估计要读取并检测的行数,注意这个不是结果集里的行数
Extra列
这一列展示的是额外信息。常见的重要值如下:
1)Using index:使用覆盖索引
覆盖索引定义:
mysql执行计划explain结果里的key有使用索引
如果select后面查询的字段都可以从这个索引的树中获取,这种情况一般可以说是用到了覆盖索引
extra里一般都有using index
覆盖索引一般针对的是辅助索引,整个查询结果只通过辅助索引就能拿到结果,不需要通过辅助索引树找到主键,再通过主键去主键索引树里获取其它字段值
如下图所示:
2)Using where:使用 where 语句来处理结果,并且查询的列未被索引覆盖
如下图所示:
3)Using index condition:查询的列不完全被索引覆盖,where条件中是一个前导列的范围
如下图所示:
4)Using temporary:MySQL需要创建一张临时表来处理查询
出现这种情况一般是要进行优化的,首先是想到用索引来优化
1. actor.name 没有索引,此时创建了张临时表来distinct
如下图所示:
2. film.name 建立了 idx_name 索引,此时查询时 extra 是 using index,没有用临时表
如下图所示:
5)Using filesort:
使用外部排序而不是索引排序,数据较小时从内存排序,否则需要在磁盘完成排序
这种情况下一般也是要考虑使用索引来优化的
1. actor.name未创建索引,会浏览actor整个表,保存排序关键字name和对应的id,然后排序name并检索行记录
如下图所示:
2. film.name建立了idx_name索引,此时查询时extra是using index
如下图所示:
6)Select tables optimized away:使用某些聚合函数(比如 max、min)来访问存在索引的某个字段
如下图所示:
索引最佳实践
示例表:(索引最佳实践中的示例都会用到)
1. 全值匹配
示例1:
示例2:
示例3:
2. 最左前缀法则
如果索引了多列(即:联合索引),要遵守最左前缀法则
指的是查询从联合索引的最左前列开始并且不跳过索引中的列
如果跳过了索引中的最左前列,则此查询不会使用联合索引
示例1:遵守最左前缀法则,使用了索引
示例2:跳过了索引中的最左前列,不会使用联合索引
3. 不要在索引列上做任何操作(计算、函数、(自动or手动)类型转换)
在索引列上做操作会导致索引失效而转向全表扫描
示例1:未在索引列上做操作,索引使用正常
示例2:在索引列上进行类型转换,索引失效
示例3:
1) 给hire_time增加一个普通索引
2) 转化时间类型,索引失效
3) 改为时间范围查询,有可能会走索引
4) 还原最初的索引状态
4. 范围条件右边之后的列不能使用索引
如下图所示:
5. 尽量使用覆盖索引(只访问索引的查询(索引列包含查询列)),减少 select * 语句
示例1:使用select * 语句
示例2:使用覆盖索引
6. 使用不等于(!= 或者 <>),not in ,not exists 可能不会使用索引导致全表扫描
< 小于、 > 大于、 <=、>= 等这些,MySQL内部优化器会根据检索比例、表大小等多个因素整体评估是否使用索引
如下图所示:
7. 使用 is null , is not null 一般情况下也无法使用索引
如下图所示:
8. like以通配符开头('$abc...')mysql索引失效会变成全表扫描操作
如下图所示:模糊左匹配
9. 字符串不加单引号,索引失效
如下图所示:字符串不加单引号,索引失效
10. 使用 or 或 in 时,不一定使用索引
如下图所示:使用or查询,未使用索引
MySQL内部优化器会根据检索比例、表大小等多个因素整体评估是否使用索引
查询优化方法可参考 11. 范围查询优化
11. 范围查询优化
1)给年龄字段添加索引,但范围查询时并未使用索引
2)上边没走索引原因:
MySQL内部优化器会根据检索比例、表大小等多个因素整体评估是否使用索引
比如上边的示例,可能是由于单次数据量查询过大导致优化器最终选择不走索引
3)优化方法:可以将大的范围拆分成多个小范围
索引使用总结:
各种 where 语句联合索引是否被使用总结:
如下图所示:
like KK% 相当于 =常量,%KK 和 %KK% 相当于 查询范围
0 条评论
下一页