(1)回表:执行引擎回判断是否需要进行回表操作
    一般我们自己建的索引不管是单列索引还是联合索引,其实一个索引就对应着一颗独立的索引B+树,索引B+树的节点仅仅包含了索引里的几个字段的值以及主键值 即使根据索引树按照条件找到了需要的数据,那也仅仅是索引里的几个字段的值和主键值,万一使用了 select 还需要很多其他的字段,那就还需走一个回表操作,根据主键跑到主键的聚簇索引里去找,聚簇索引的叶子节点是数据页,找到数据页里才能把一行数据的所有字段值提取出来。
    假设SQL语句 select
    from table order by xx1,xx2,xx3的语句,得从联合索引的索引树里按照顺序取出所有数据,接着每一条数据都走一个主键的聚簇索引查找,其实性能也是不高的,有时候MySQL的执行引擎甚至认为, select from table order by xx1,xx2,xx3的语句,相当于把联合索引和聚簇索引,两个索引的所有数据都扫描一遍,那还不如直接全表扫描的了,这就扫描了一个索引。
    但是如果SQL语句 select
    from table order by xx1,xx2,xx3 limit 10 这样的语句,执行引擎就知道,先扫描联合索引的索引树拿到10条数据,接着对10条数据在聚簇索引里查找10次就可以了,这样就还是走联合索引的。

    (2)覆盖索引:不需要回表的直接在二级索引查询后就能查到所需字段的 情况 是 覆盖索引。
    覆盖索引不是一种索引,他就是基于索引查询的方式罢了,意思是针对类似 select xx1,xx2,xx3 from table order by xx1,xx2,xx3 这样的语句,这种情况下,仅仅需要联合索引的几个字段的值,其实只需要扫描联合索引就可以了,不需要回表去聚簇索引里找其他字段。所以需要的字段值直接在索引树里就能提取出来,不需要回表到聚簇索引,这种查询方式就是覆盖索引。

    (3)总结:尽量避免全表扫描
    也正是这样,在写SQL语句的时候,一方面需要注意也许会用到联合索引,但是是否可能会导致大量的回表到聚簇索引,如果回表到聚簇索引的次数太多,可能直接就成了全表扫描不走联合索引了。另一方面,尽可能还是在SQL里指定你仅仅需要的几个字段,不要搞一个 select * 把所有字段都查出来,甚至最好直接走覆盖索引的方式,不要回表到聚簇索引。即使真的回表到聚簇索引,那也尽可能用limit,where之类的语句限定一下回表到聚簇索引的次数,就从联合索引里筛选少数数据,然后再回到聚簇索引里,这样性能也会好些。

    知识点:先在普通索引页查询到所有主键id,在主键id一个一个去主键索引页上查找数据页对应的行数据,凡是在内存中的最后返回给客户端
    知识点:mysql会进行优化查出来多条后一次性的回表查询 MMR 优化。
    什么是MMR?
    MMR全称:Muti-Range-Read,是MySQL5.6优化器的一个新特性,优化的功能在使用二级索引做范围扫描的过程中减少磁盘随机IO和减少主键索引的访问次数,将随机IO转换为顺序IO 。
    MMR的开启参数在 optimizer_switch系统变量里,有两个参数控制,mrr需要设置为ON,mrr_cost_base如果设置为on是基于成本控制,off表示尽可能去使用mrr,mysql5.6默认是开启的状态。