覆盖索引

特点:

  • 覆盖索引可以很好的避免mysql的回表操作,进行性能优化
  • 由于覆盖索引可以减少树的搜索次数,显著提升查询性能,所以使用覆盖索引是一个常用的性能优化手段。什么是回表?
  • 回表指的是 在普通索引树中找到对应的主键索引,在去主键索引树中找对应的数据,这个操作叫做回表
  • 因为前面我们就已经直达 mysql 会有一个主键索引 b+tree ,创建一个普通索引就会对应生成一个 b+tree 的索引树什么是覆盖索引?
  • mysql进行查询操作的时候,所要查询的字段刚好就在索引上面,查询的时候用到了该索引,那么,mysql就不用再去进行回表操作了,能直接将对应的索引字段返回
  • 即: SQL只需要通过索引就可以返回查询所需要的数据,而不必通过二级索引查到主键之后再去查询数据。例:

    • 有如下表
      1. create table T (
      2. ID int primary key,
      3. k int NOT NULL DEFAULT 0,
      4. s varchar(16) NOT NULL DEFAULT '',
      5. index k(k))engine=InnoDB;
      6. insert into T values(100,1, 'aa'),(200,2,'bb'),(300,3,'cc'),(500,5,'ee'),(600,6,'ff'),(700,7,'gg');
      覆盖索引.png
  • 执行语句

    1. select * from T where k between 3 and 5;
    • 现在,我们一起来看看这条 SQL 查询语句的执行流程:
      1. 在 k 索引树上找到 k=3 的记录,取得 ID = 300;
      2. 再到 ID 索引树查到 ID=300 对应的 R3;
      3. 在 k 索引树取下一个值 k=5,取得 ID=500;
      4. 再回到 ID 索引树查到 ID=500 对应的 R4;
      5. 在 k 索引树取下一个值 k=6,不满足条件,循环结束

    在这个过程中,回到主键索引树搜索的过程,我们称为回表。可以看到,这个查询过程读了 k 索引树的 3 条记录(步骤 1、3 和 5),回表了两次(步骤 2 和 4)。 如果执行的语句是

    1. select ID from T where k between 3 and 5;

这时只需要查 ID 的值,而 ID 的值已经在 k 索引树上了,因此可以直接提供查询结果,不需要回表。也就是说,在这个查询里面,索引 k 已经“覆盖了”我们的查询需求,我们称为覆盖索引。

最左前缀原则

  • 一般出现在联合索引中例:

    • MySQL中的索引可以以一定顺序引用多列,这种索引叫作联合索引。
    • 如User表的name和city加联合索引就是(name,city),而最左前缀原则指的是,如果查询的时候查询条件精确匹配索引的左边连续一列或几列,则此列就可以被用到。 如下:

      1. select * from user where name=xx and city=xx ; //可以命中索引
      2. select * from user where name=xx ; //可以命中索引
      3. select * from user where city=xx ; //无法命中索引
    • like 语句也是如此,当 %在右边的时候,是有可能命中索引的,当%在左边的时候是不会命中索引的,将全表扫描.

    • 我们可以根据最左前缀原则可以进行很好的管理索引,减少不必要的索引

索引下推

  • 索引下推(index condition pushdown )简称ICP,在Mysql5.6的版本上推出,用于优化查询。
  • 当where条件中的字段包含在索引中,可以在查找索引树的时候就可以进行适当的条件过滤,符合where条件的才会得到返回例子:

    • 在开始之前先先准备一张用户表(user),其中主要几个字段有:id、name、age、address。建立联合索引(name,age)。
    • 假设有一个需求,要求匹配姓名第一个为陈的所有用户,sql语句如下:

      1. SELECT * from user where name like '陈%';
      • 根据 “最佳左前缀” 的原则,这里使用了联合索引(name,age)进行了查询,性能要比全表扫描肯定要高。
    • 问题来了,如果有其他的条件呢?假设又有一个需求,要求匹配姓名第一个字为陈,年龄为20岁的用户,此时的sql语句如下:

      1. SELECT * from user where name like '陈%' and age=20;
    • 这条sql语句应该如何执行呢?下面对Mysql5.6之前版本和之后版本进行分析。Mysql5.6之前的版本

      • 5.6之前的版本是没有索引下推这个优化的,因此执行的过程如下图:5.6之前.jpeg
      • 会忽略age这个字段,直接通过name进行查询,在(name,age)这课树上查找到了两个结果,id分别为2,1,然后拿着取到的id值一次次的回表查询,因此这个过程需要回表两次

        Mysql5.6之后的版本

      • 5.6版本添加了索引下推这个优化,执行的过程如下图:

        1. ![5.6之后.jpeg](https://cdn.nlark.com/yuque/0/2021/jpeg/12428142/1610432995379-0fd9c781-edfd-4578-afeb-399c30a2f6a3.jpeg#align=left&display=inline&height=281&margin=%5Bobject%20Object%5D&name=5.6%E4%B9%8B%E5%90%8E.jpeg&originHeight=281&originWidth=720&size=23484&status=done&style=none&width=720)
      • InnoDB并没有忽略age这个字段,而是在索引内部就判断了age是否等于20,对于不等于20的记录直接跳过,因此在(name,age)这棵索引树中只匹配到了一个记录,此时拿着这个id去主键索引树中回表查询全部数据,这个过程只需要回表一次。