本章主要对mysql中和索引相关的知识进行梳理,主要就explain的结果进行分析,提供实例讲解mysql的执行计划中索引的使用细节
区分 Using Where、Using Index、 Using Index Condition、Using fileSort、 Using temporary的区别
1.mysql索引,聚集索引和二级索引
索引的定义:是为了提高数据的访问性能,提供的附加存储的辅助字段,在实现方式上多为B\B+树,也有少数使用Hash等。
聚簇索引是对磁盘上存储的数据重新组织,按照一个指定列或多个列的值排序,存储的数据顺序和索引的顺序一致。
非聚簇索引:即辅助索引,索引的叶子节点为主键索引的id值,使用二级索引查找数据,如列不能覆盖,需要通过主键索引回表查询,所以叫做二级索引。
2.explain执行任务分析
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
- id:这是SELECT的查询序列号,每个 SELECT 都会自动分配一个唯一的标识符
- select_type: SELECT 查询的类型
SIMPLE:简单的select查询,查询中不包括子查询或者UNION
PRIMARY:查询中包含子查询,最外层查询则被标记为
SUBQUERY:在select或where列表中包含了子查询
DERIVED:在from列表中包含的子查询被标记为DERIVED(衍生)MySQL会递归执行这些子查询,把结果放在临时表里。
UNION:若第二个select出现在union之后,则被标记为union;如果union包含在from子句的子查询中,外层select将被标记为:DERIVED - table 查询表名
- type 索引的使用方式,一般认为执行效率 为 system>const>eq_ref>ref>range>index>ALL
system system:表只有一行记录,忽略
const 表示通过索引一次就找到了(主键索引或唯一索引),只匹配一行数据
eq_ref 唯一性索引扫描在联表查询中,通常出现在多表的 join 查询, 表示对于前表的每一个结果, 都只能匹配到后表的一行结果
ref 非唯一性索引扫描,虽然使用了索引,但该索引列的值并不唯一,有重复
range 指的是有范围的索引扫描,相对于index的全索引扫描 > < in or
index 和 ALL 类型类似, 只不过 ALL 类型是全表扫描, 而 index 类型则仅仅扫描所有的索引
ALL 全表扫描 - possible_keys: 此次查询中可能选用的索引
- key: 此次查询中确切使用到的索引
- key_len 索引长度 到了索引,key_len的长度都是这个索引的整体长度
- ref ref 字段显示了哪些字段或者常量被用来和 key配合从表中查询记录出来
- rows:需要扫描的行数
- Extra 包含MySQL解决查询的详细信息
3.extra字段分析
对extra的索引情况分析,主要从4个角度来分析
1.是否走索引
2.是索引命中还是区间,需要索引扫描
3索引是否可以覆盖查询的字段,是否要回表查询
4是否需要额外的排序
以Music数据表(索引包括 IDX_PublishTime 、idx_dt_sts_ptime_suspectType)
1.Using Index
走索引 - 索引命中 -覆盖所有查询字段 - 无需排序(或无需额外排序)
Using index 直接通过索引就能定位数据,且不用回表
explain select id from Music where PublishTime=1590854400000;
+----+-------------+-------+------------+------+-----------------+-----------------+---------+-------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+-----------------+-----------------+---------+-------+------+----------+-------------+
| 1 | SIMPLE | Music | p20200601 | ref | IDX_PublishTime | IDX_PublishTime | 8 | const | 1 | 100.00 | Using index |
2.Using Index Condition
走索引 - 索引范围扫描 -覆盖所有查询字段 - 无需排序(或无需额外排序)
explain select content from Music where DataType=3 and Status in(A1,A2,A3,A4) and PublishTime>=1590854400000
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+-----------------------------------------------------------------------+-------+---------------------------------------------------------------------------------------------------------------------------------------+------------------------------+---------+------+------+----------+-----------------------+
| 1 | SIMPLE | Music | p20200601,p20200701,p20200801,p20200901,p20201001,p20201101,p20201201 | range | IDX_PublishTime,idx_dt_sts_ptime_suspectType | idx_dt_sts_ptime_suspectType | 16 | NULL | 4986 | 100.00 | Using index condition |
3.Using Where
using where 的概念维度比较广泛,既可以在使用索引的情况下也可以在不使用索引的情况下。定义是使用where条件进行过滤。唯一的是可以和完全使用索引进行区分开。
4.NULL
NULL则是索引能定位数据,但是需要回表
走索引 - 索引命中 -回表查询 - 无需排序(或无需额外排序)
以 1. Using Index为例,select id 改成 select content 就变成了 Extra=Null .
explain select content from Music where PublishTime=1590854400000;
+----+-------------+-------+------------+------+-----------------+-----------------+---------+-------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+-----------------+-----------------+---------+-------+------+----------+-------+
| 1 | SIMPLE | Music | p20200601 | ref | IDX_PublishTime | IDX_PublishTime | 8 | const | 1 | 100.00 | NULL |
+----+-------------+-------+------------+------+-----------------+-----------------+---------+-------+------+----------+-------+
5.Using filesort
一般出现在需要系统额外进行一遍排序,无法使用索引进行排序的情况下。
最常见的情况是 tab (idx(a,b,c))
select * from tab where a= and b in () order by c;
-----在b列范围查询了,即使是前缀索引,order by c也无法使用索引。其右边的字段用不到了,只能用到 ab列 b 列索引扫描,select * 肯定要回表。order by 需要额外排序
即 走索引 - 索引范围扫描 -回表查询 - 需排序(需额外排序)
using index condition using filesort
不需回表,但需要索引范围的扫描,两个范围查询,排序需要额外排序
explain select id from Music where DataType=3 and Status in (A1,A2,A3,A4) and PublishTime>=1590854400000 order by PublishTime desc;的extra 为
Using where; Using index; Using filesort
走索引 - 索引范围扫描 - 不需回表 - 额外排序
这里要解释下为什么会出现Using where?不是包含了 idx_dt_sts_ptime_suspectType 的前缀了吗
还是因为要排序的原因。因为status 作为前缀使用了in 范围查找,那么PublishTime的排序就无法再使用索引了。这里一定要注意是因为排序和前缀列使用了范围查找,所以才导致后续的前缀索引字段无法使用索引排序
正因此,无法使用索引的后缀部分使用了using where 查询,额外排序则使用using filesort
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+-----------------------------------------------------------------------+-------+---------------------------------------------------------------------------------------------------------------------------------------+------------------------------+---------+------+------+----------+------------------------------------------+
| 1 | SIMPLE | Music | p20200601,p20200701,p20200801,p20200901,p20201001,p20201101,p20201201 | range | IDX_PublishTime,idx_dt_sts_ptime_suspectType | idx_dt_sts_ptime_suspectType | 16 | NULL | 5049 | 100.00 | Using where; Using index; Using filesort |
为了验证猜测,是order by PublishTime desc 排序导致 无法走索引,必须using where,我们去掉排序字段验证下
ken_len 16, 索引 PublishTime bigint 8字节,dataType 和status int 各自4字节,没有null
ken_len 是16,说明即使使用了 order by 且在排序的索引字段前使用了In范围查询,查询数据还是会完整的走索引
6.Using where Using index
即使在前缀索引都覆盖到的情况下,有字段使用了范围查找,也会引入using where,出现Using where Using index意味着是通过索引扫描(或者表扫描)来实现sql语句执行的
1.查询的列被索引覆盖,并且where筛选条件是索引列之一但是不是索引的不是前导列;
2.查询的列被索引覆盖,并且where筛选条件是索引列前导列的一个范围 此处的返回
explain select id from Music where DataType=3 and Status in(A1,A2,A3,A4) and PublishTime>=1590854400000;
| 1 | SIMPLE | Music | p20200601,p20200701,p20200801,p20200901,p20201001,p20201101,p20201201 | range | IDX_PublishTime,idx_dt_sts_ptime_suspectType | 16 | NULL | 19579 | 100.00 | Using where; Using index
比较Using where Using index 和 Using Index Condition,其实没有明显的可比性,最终还是要看具体的执行环境。
但对于同一条sql来说,这两者的相同点 是都是要进行索引扫描,无法直接命中索引。区别是 Using Index Condition肯定要回表查询,而Using where Using index 则不需要回表查询。