回表和索引覆盖

简介
mysql的innodb引擎查询时无法使用索引覆盖的场景下,需要做回表操作获取记录所需要的字段。Innodb有两大类索引,一类是集聚索引(Clustered Index),一类是普通索引(Secondary Index)。

聚集索引决定了数据库的物理存储结构,而主键只是确定表格逻辑组织方式。这两者不可混淆!

InnoDB的集聚索引
InnoDB集聚索引的叶子节点存储行记录,因此InnoDB必须有且只有一个集聚索引

  • 如果一个主键被定义了,那么这个主键就是作为聚集索引
  • 如果没有主键被定义,那么该表的第一个唯一非空索引被作为聚集索引
  • 如果没有主键也没有合适的唯一索引,那么innodb内部会生成一个隐藏的主键作为聚集索引ROWID,这个隐藏的主键是一个6个字节的列,改列的值会随着数据的插入自增。
  • 自增主键会把数据自动向后插入,避免了插入过程中的聚集索引排序问题。聚集索引的排序,必然会带来大范围的数据的物理移动,这里面带来的磁盘IO性能损耗是非常大的。
    而如果聚集索引上的值可以改动的话,那么也会触发物理磁盘上的移动,于是就可能出现page分裂,表碎片横生。所以不应该修改聚集索引。

InnoDB的普通索引
InnoDB普通索引的叶子节点存储主键值(MyISAM则是存储的行记录头指针)

什么是回表查询:
假设有个t表(id PK, name KEY, sex, flag),这里的id是聚集索引,name则是普通索引。

表中有四条记录:
回表和索引覆盖_第1张图片
集聚索引的B+树索引(id是PK,叶子节点存储行记录)
回表和索引覆盖_第2张图片
普通索引B+树索引(name是KEY,叶子节点存储PK的值,即id):
回表和索引覆盖_第3张图片
普通索引因为无法直接定位行记录,其查询过程在通常情况下是需要扫描两遍索引树的。

select * from t where name = 'lisi';

这里执行的情况是这样的:
回表和索引覆盖_第4张图片
粉红色的路径需要扫描两遍索引树,第一遍先通过普通索引定位到主键id=5,然后第二遍再通过集聚索引定位到具体行记录。这就是回表查询,即先定位到主键值,再根据主键值定位行记录,性能相对于只扫描一遍的集聚索引树性能要低一些、

索引覆盖
索引覆盖是一种避免回表查询的优化策略。具体的做法是将要查询的数据作为索引建立普通索引(可以是单列索引,也可以是一个索引语句定义所有要查询的列,即联合索引),这样的话就可以直接返回索引中的数据,不需要再通过集聚索引去定位行记录,避免了回表的情况发生。

覆盖索引的定义与注意事项
如果一个索引覆盖(包含)了所有需要查询的字段的值,这个索引就是覆盖索引。因为索引中已经包含了要查询的字段的值,因此查询的时候直接返回索引中的字段值就可以了。不需要再到表中查询,避免了对主键索引的二次查询,也就提高了查询的效率。

要注意的是,不是所有类型的索引都可以成为覆盖索引的,因为覆盖索引必须要存储索引的列值,而哈希索引,空间索引和全文索引都不存储索引列值,索引Mysql只能使用B-tree索引做覆盖索引。

覆盖索引的优点:
1,索引条目通常远小于数据行的大小,因为索引覆盖只需要读取索引,极大地减少了数据的访问量。
2.索引是按照列值顺序存储的,对于IO密集的范围查找会比随机从磁盘读取每一行数据的IO小很多。

3.一些存储引擎比如MyISAM在内存中只缓存索引,数据则依赖操作系统来缓存,因此要访问数据的话需要一次系统调用,使用覆盖索引则避免了这一点。

4.由于InnoDB的聚簇索引,覆盖索引对InnoDB引擎下的数据库表特别有用。因为InnoDB的二级索引在叶子节点中保存了行的主键值,如果二级索引能够覆盖查询,就避免了对主键索引的二次查询。

你可能感兴趣的:(数据库)