知识卡片
覆盖索引:让查询只读索引,无须回表
内容
索引的常规用法是”先靠索引找到行的位置,再回表读整行数据”,但如果一个索引的
叶子节点里已经包含了这条查询需要的所有列,就没有必要再多走一次回表这一步——
这种”索引本身就能覆盖查询全部所需字段”的情况称为”覆盖索引”。它带来的收益
不只是省了一次磁盘寻址:索引条目通常远小于完整数据行,只读索引意味着更少的
数据拷贝、更容易被整体放进内存缓存,而且索引是按列值有序存储的,范围查询走
覆盖索引比按行随机回表要少得多的随机I/O。这个机制对
[[InnoDB聚簇索引让二级索引必须携带主键因此主键要尽量小]]描述的InnoDB二级
索引结构格外有利:既然二级索引的叶子节点本来就带着主键值,如果查询恰好只需
要”二级索引列+主键”这些字段,就能完全避免对主键聚簇索引的二次查询——这也是
“二级索引必须携带主键值”这个设计代价在覆盖索引场景下反过来变成收益的一个
例子。覆盖索引并非对所有索引类型都适用:哈希索引、空间索引、全文索引都不
存储列值本身,只有B-Tree索引能做覆盖索引。判断一个查询是否真的用上了覆盖
索引,看EXPLAIN的Extra列是否出现”Using index”(而不是”Using where”),而
不能只看查询用到了哪个索引就想当然。还有一个容易被忽视的陷阱:即使WHERE
条件的字段被索引覆盖了,只要SELECT列表里选了未被该索引覆盖的其他列(比如
SELECT *),MySQL 5.5及更早版本判断出WHERE条件为假、这一行注定要被过滤掉
时,仍然会先回表取出整行数据,再把它丢弃——覆盖索引的”部分覆盖”不会自动
省下这次多余的回表。
参考来源
- 位置:《高性能MySQL:第3版》第5章"创建高性能的索引"5.3.6节"覆盖索引"
(源文件:_epub-src/OEBPS/Text/part0012.xhtml)
- 结论依据:原文明确"如果一个索引包含(或者说覆盖)所有需要查询的字段的
值,我们就称之为'覆盖索引'……由于InnoDB的聚簇索引,覆盖索引对InnoDB表
特别有用。InnoDB的二级索引在叶子节点中保存了行的主键值,所以如果二级
主键能够覆盖查询,则可以避免对主键索引的二次查询……哈希索引、空间索引
和全文索引等都不存储索引列的值,所以MySQL只能使用B-Tree索引做覆盖
索引",直接说明覆盖索引的定义、对InnoDB的特殊价值及适用索引类型限制。
- 原始内容:如果索引的叶子节点中已经包含要查询的数据,那么还有什么必要
再回表查询呢?如果一个索引包含(或者说覆盖)所有需要查询的字段的值,
我们就称之为"覆盖索引"。