Mysql

关注公众号 jb51net

关闭
首页 > 数据库 > Mysql > mysql覆盖索引与索引下推

MySQL覆盖索引与索引下推详解

作者:银发控、

这篇文章给大家介绍MySQL覆盖索引与索引下推的相关知识,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧

索引数据在内存里的命中率通常比表数据高是为什么?

索引比数据更小、更紧凑、访问频率更高,所以它在内存里的“存活率”天然比表数据高。

下面从几个维度拆解原因:

索引比表数据“小”,同样内存能装更多

结果:同样大小的 Buffer Pool,能缓存的索引页数量远多于数据页数量,索引页被淘汰的概率更低。

查询总是先经过索引(索引的“入口”地位)

几乎所有的查询(除了全表扫描)都是先走索引

  1. 先在索引树里定位到主键值。
  2. 再通过主键去聚簇索引拿数据。

因此:

结果:索引页的访问频率天然高于数据页,LRU 算法会把高频页留在内存里。

索引访问更集中(热点更集中)

结果:索引的热点集中在少数页上,更容易长期驻留内存;数据的热点分散,很多页虽然被访问但频率不够高,容易被淘汰。

索引的访问模式更“规律”

结果:索引页更容易被批量加载并留在内存里。

举例对比

假设一张 user 表有 1000 万行,每行 200 字节,主键是 id,有一个二级索引 idx_name

如果 Buffer Pool 能存 10 万页:

实际运行时,索引页全部在内存,数据页部分在内存,命中率自然不一样。

总结

原因说明
体积小索引页更紧凑,同样内存能装更多页。
访问频率高每条查询都走索引,索引页被反复访问。
热点集中索引树的根/中层节点是全局热点,永不淘汰。
访问模式连续索引扫描连续,预读命中率高。

所以,你在调优数据库时,如果发现 Buffer Pool 不够大,优先保证索引能被完全缓存,这是提升查询性能的关键。

这个 SQL 在做什么?

EXPLAIN SELECT last_name, first_name, hire_date
FROM employees
WHERE last_name = '鸭';

加上 EXPLAIN 后,MySQL 不会返回真正的员工数据,而是返回一张表,描述它执行计划的每一步。其中有一列叫 Extra,如果这一列显示 Using index,就意味着这个查询使用了覆盖索引,显示Using index condition就意味着使用了索引下推,需要回表但次数减少了。

要理解索引下推,必须先看 MySQL 的“大楼”是怎么分工的。

Server 层(大脑/指挥官

这是 MySQL 的上层部分,包含了连接器、查询缓存、分析器、优化器、执行器等。

存储引擎层(手脚/仓库管理员)

这是 MySQL 的下层部分,最常用的是 InnoDB

什么是“回表”?(背景知识)

在理解下推前,要懂回表。
假设你有一个联合索引 (name, age),但你查询的是 select *。

  1. 引擎先去索引树里找到 name 匹配的记录。
  2. 但索引树里通常不包含所有字段(比如没有地址、电话),于是引擎要根据主键 ID,回主表再查一次,拿到完整记录。这就是回表回表是很耗时的(涉及磁盘 I/O)

什么是索引下推(ICP)?
索引下推的核心就是:能让存储引擎在找数据的时候多做点事,尽量减少回表的次数。

1. 没有索引下推时(MySQL 5.6 之前)

2. 有了索引下推时(MySQL 5.6 之后)

为什么要强调“主要用在联合索引上”?

这是因为索引下推利用的是索引中已经存在但无法直接通过索引定位(Range Scan)的列

举个具体例子:
假设索引是 (name, age)。
SQL:

SELECT * FROM user WHERE name LIKE '张%' AND age = 20;

但又有一个坑,如果把SQL改为:
SQL:

SELECT * FROM user WHERE name LIKE '%张%' AND age = 20;

就因为左模糊查询(以 % 开头)导致索引“失效”了,存储引擎连快速定位“起点”的机会都没有了,无法实现“索引寻址”(Index Seek):只能变成“索引扫描”(Index Scan)或全表扫描。
核心原因:B+ 树是按“顺序”排列的

数据库的索引(B+ 树)就像一本排好序的字典。
如果你建立了一个联合索引 (name, age),在索引树里,数据是这样排列的:

场景 A:WHERE name LIKE ‘张%’(前缀匹配)

场景 B:WHERE name LIKE ‘%张%’(全模糊/左模糊)

为什么这时候“索引下推”救不了场?

虽然理论上,存储引擎在“全表扫描”或者“全索引扫描”的过程中,也可以顺便判断 age=20,但这种情况通常不叫“索引下推”的典型应用,原因如下:

一旦决定全表扫描,就不存在所谓的“利用二级索引过滤数据”了。
再看这样的例子

索引下推(ICP)生效的前提是:必须先有“一部分索引”能被用来“定位(Range Scan/Ref)”数据的起始范围。

让我们对比一下刚才问的例子和图片里的例子:

  1. 刚才的例子:WHERE name LIKE ‘%张%’
  1. 图片里的例子:WHERE zipcode=‘95054’ AND lastname LIKE ‘%etrunia%’

总结:两者的本质区别

查询条件索引能否“进门” (定位)索引下推能否介入核心原因
name LIKE ‘%张%’不能不能 (或没意义)索引的第一列就无法定位,必须扫全表。
zipcode=‘95054’ AND lastname LIKE ‘%张%’可以 (靠 zipcode)可以zipcode 帮引擎进场了,进场后发现 lastname 就在手边,顺便滤掉。
换个生活化的比喻:

结论:
索引下推是为了在已经缩小了的索引范围内,进一步利用索引里的其他列进行过滤。如果连第一步“缩小范围”都做不到,索引下推就无从谈起。

总结

  1. Server 层: 负责决策、过滤、计算。
  2. 存储引擎层: 负责读写、根据索引找 ID。
  3. 索引下推(ICP): 把原本属于 Server 层的“过滤”逻辑,下放到了存储引擎层。
  4. 核心价值: 减少回表次数,减少磁盘 I/O,提升查询性能。
    一句话总结:
    以前是“把东西都搬回家再挑”,现在是“在仓库挑好了再搬回家”。

到此这篇关于MySQL覆盖索引与索引下推的文章就介绍到这了,更多相关mysql覆盖索引与索引下推内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文