还傻傻分不清 MySQL 回表查询与索引覆盖?
hello,大家好,我是张张,「架构精进之路」公号作者。
最近的工作中,遇到一个查询里用到主键索引与二级索引并存的问题情况,那对于这种情况,索引是如何高效执行的,是否会产生回表查询呢?
等等,首先解释一下,什么是回表?
回表定义:先索引扫描,再通过 ID 去取索引中未能提供的数据,即为回表。
即先定位主键值,再定位行记录。
1、两类索引
为了更好地阐释这个问题,我们还是从索引来介绍吧。
InnoDB 索引分为两大类,一类是聚集索引(Clustered Index),一类是非聚集索引(Secondary Index)
1.1 聚集索引(聚簇索引)
InnoDB 聚集索引的叶子节点存储行记录,因此 InnoDB 必须要有且只有一个聚集索引。
如果表定义了 PK(Primary Key,主键),那么 PK 就是聚集索引。
如果表没有定义 PK,则第一个 NOT NULL UNIQUE 的列就是聚集索引。
否则 InnoDB 会另外创建一个隐藏的 ROWID 作为聚集索引。
这种机制使得基于 PK 的查询速度非常快,因为直接定位的行记录。
1.2 非聚集索引(普通索引、非聚簇索引、二级索引)
普通索引也叫二级索引,除聚簇索引外的索引,即非聚簇索引。
InnoDB 的普通索引叶子节点存储的是主键(聚簇索引)的值,而 MyISAM 的普通索引存储的是记录指针。
Q:为什么非主键索引结构叶子结点存储的是主键值?
A:减少了出现行移动或者数据页分裂时二级索引的维护工作(当数据需要更新的时候,二级索引不需要修改,只需要修改聚簇索引,一个表只能有一个聚簇索引,其他的都是二级索引,这样只需要修改聚簇索引就可以了,不需要重新构建二级索引)
在使用非聚集索引时,为了取到具体数据,则需要通过 PK 回到聚集索引里去查询数据。这就叫回表查询,扫描了 2 次索引树,所以效率相对较低。
2、应用示例
一例胜千言,show me you code!
2.1 建表操作
id 字段是聚簇索引,name 字段是普通索引(二级索引)
2.2 填充数据
2.3 索引结构
聚簇索引(ClusteredIndex)
id 是主键,所以是聚簇索引,其叶子节点存储的是对应行记录的数据
普通索引(secondaryIndex)
name 是普通索引(二级索引),非聚簇索引,其叶子节点存储的是聚簇索引的的值
2.4 查找过程
普通索引查找过程
如果查询条件为主键(聚簇索引),则只需扫描一次 B+树即可通过聚簇索引定位到要查找的行记录数据。
普通索引因为无法直接定位行记录,其查询过程在通常情况下是需要扫描两遍索引树的。
实际执行过程:
路径需要扫描两遍索引树,第一遍先通过普通索引定位到主键值 id=5,然后第二遍再通过聚集索引定位到具体行记录。
这就是所谓的回表查询,即先定位主键值,再根据主键值定位行记录,性能相对于只扫描一遍聚集索引树的性能要低一些。
3、索引覆盖
索引覆盖是一种避免回表查询的优化策略。
只需要在一棵索引树上就能获取 SQL 所需的所有列数据,无需回表,速度更快。
3.1 如何实现覆盖索引
将要查询的数据作为索引列建立普通索引(可以是单列索引,也可以一个索引语句定义所有要查询的列,即联合索引),这样的话就可以直接返回索引中的的数据,不需要再通过聚集索引去定位行记录,避免了回表的情况发生。
explain 分析:因为 name 是普通索引,使用到了 name 索引,通过一次扫描 B+树即可查询到相应的结果,这样就实现了覆盖索引
希望今天的讲解对大家有所帮助,谢谢!
Thanks for reading!
作者:张张,十年研发风雨路,大厂架构师,「架构精进之路」专注架构技术沉淀学习及分享,职业与认知升级,坚持分享接地气儿的干货文章,期待与你一起成长。
关注并私信我回复“01”,送你一份程序员成长进阶大礼包,欢迎勾搭。
版权声明: 本文为 InfoQ 作者【架构精进之路】的原创文章。
原文链接:【http://xie.infoq.cn/article/9bae797cd02bb7361c262838f】。文章转载请联系作者。
评论