在 InnoDB 中,从二级索引回到主键索引查询数据,这个过程称作回表过程,回表过程是可以优化的,这个优化就是利用覆盖索引。
先说结论,如果一个索引的字段包含了所有要查询的字段,这个索引就称作覆盖索引,覆盖索引可以减少回表过程,能有效提高查询效率。
在 InnoDB 中数据都是保存在 B+ 树上,主键索引保存了整行记录,二级索引保存了主键的值。

一次查询操作,要么是遍历主键索引,要么是遍历二级索引,要么就是先遍历二级索引得到主键 id 的值,然后再到主键索引上通过主键 id 查找满足要求的记录。
如果只遍历一次 B+ 树就能获取到我们要的数据,即没有回表过程,这个效率是不错的,这就是覆盖索引的优势。

create table user(
id int(11) primary key, 
name varchar(20) not null, 
age int(11),
index (age)) engine=InnoDB;
数据
1    bob 16
2    kom 19
3    gum 18
4    tt  20
5    yy  25

select name from user where age between 18 and 21

我们来分析下这条 sql 的执行过程:
1、age 字段上有索引,mysql 会先到 age 字段的 B+ 树上找到满足条件的第一个叶子节点(age=19),这个叶子节点上保存了对应主键 id 的值 2,然后再到主键索引上找到 id 为 2 的这条记录,同时把 name 字段拿出来。
2、重复第一步的操作,继续从 age 索引上的叶子节点往后遍历找出满足条件的第二个叶子节点,同样回到主键上拿出 name 字段的值,直到遍历到不满足条件的叶子节点(age=25)。

也就是说,这条 sql 语句虽然用到了索引,但是 age 索引上并没有要查询的 name 字段,所以只能回表到主键索引上查出 name 字段,所以这个过程其实是遍历了个两个 B+ 树。
删除 age 这个单列索引,创建一个覆盖索引 (age,name), 把要查询的 name 字段也添加到索引。
由于现在这个覆盖索引上的字段包含了要查询的 age 和 name 字段,免去了到主键索引上查询数据的过程,其实也就是只遍历了一个 B+ 树,可以大大提升查询效率。