覆盖索引 & 索引条件下推

先建一张表,后面会用到:

覆盖索引

当执行一条查询语句时,MySQL 会选择最优的执行方案,无非就是选择全表扫描还是使用某个索引执行

如果选择某个索引执行,当查询列表中包含索引列之外的字段时,查询到结果后还需要根据主键进行一次回表操作,在聚簇索引中查询到完整用户记录

覆盖索引:如果查询列表中刚好只包含索引列,那么就可以不需要回表,直接在该索引中就可以获取所有字段

如果执行下面查询:

MySQL 选择使用 idx_key 索引查询,而且需要回表将完整用户记录查询出来并返回给客户端。下面从底层介绍查询流程:

如果执行下面查询:

其实流程和上面差不多,区别就在于第二个步骤,存储引擎根据 B+ 树索引快速定位位置后,直接返回给 server 层,不需要回表,因为索引列包含了查询列表

注意:回表操作由存储引擎完成;判断条件是否满足要求由 server 层完成

索引条件下推

如果执行下面查询:

首先 MySQL 有两种执行方案的选择,第一种可以像上面那样:

总结:在该方案中,存储引擎只负责根据 server 层传来的条件定位记录,然后回表后返回完整记录,不会做任何判断

如果一条记录c2 = 'a',它不满足第二个条件,但存储引擎依旧会回表交由 server 层判断,这无疑是增加了开销,因为 c2 也属于索引列,完全可以不需要回表,直接由存储引擎判断c2 = 'a'是一条不满足搜索条件的记录

下面要介绍的第二种方案就是增加了该优化:

可以看出第二种方案可以减少回表的次数,而且回表也是开销很大的操作,这样可以降低查询语句执行的成本

总结:在该方案中,存储引擎即负责根据 server 层传来的条件定位记录,也负责判断含有索引列的条件,只有满足条件后才回表给 server 层返回完整记录

第二种方案也被称之为索引条件下推,将含有索引列的条件下推给存储引擎判断,可以减少回表次数 (注意:下推的是条件)

注意:索引条件下推仅适用于select语句,不适用于updatedelete这些需要修改记录的语句

我们通过explain命令看看上面查询语句的执行计划:

从执行计划中可以看出使用 idx_key 索引执行,Using index condition 表示使用了索引条件下推

下面再来看一条查询语句的执行计划:

这条查询和上一条查询唯一的区别就在于查询列表,本条的查询列表只包含索引列,所以可以使用覆盖索引。Using where 表示有搜索条件在 server 层判断;Using index 表示使用覆盖索引执行查询

从执行计划可以看出本查询并没有使用索引条件下推,而是使用覆盖索引,而且存储引擎并没有判断搜索条件c2 like '%b',而是由 server 层判断

虽然我个人感觉如果c2 >= 'a' and c2 like '%b'都交给存储引擎判断,满足要求后才返回给 server 层会更快,但是 MySQL 好像没有这么做,其实不需要回表这两种判断方法相差也不多!