摘要:网上大量教程只讲最左匹配口诀,很少讲底层 B+ 树、索引下推 ICP、回表、顺序/随机 IO、BufferPool 之间的完整链路。本文结合 Explain 执行计划、底层存储原理,把面试高频坑一次性讲透。
前置准备
建表与复合索引:
CREATE TABLE test(
id INT PRIMARY KEY AUTO_INCREMENT,
a INT,
b INT,
c INT
);
— 创建联合索引 idx(a,b,c)
CREATE INDEX idx_abc ON test(a,b,c);
复合索引 idx(a,b,c) 的 B+ 树叶子节点,排序规则是:优先按 a 排序;a 相等,按 b 排序;b 相等,按 c 排序;叶子行末尾附带主键 id。

上图演示了复合索引叶子页的排序逻辑。从图中可以看到:a 相同的行被分到同一组;a 相同后按 b 排序;只有 (a,b) 都相同时,c 才有序。行尾的主键 id 是后续回表的“钥匙”。
因果链:因为 InnoDB 的 B+ 树叶子行按 (a,b,c) 全局有序 → 所以索引定位时可以从 a 开始二分查找 → 但因为排序优先级是 a→b→c 依次递减 → 所以一旦 a 或 b 出现范围查询,c 在叶子内部就不再有序。
一、什么是最左匹配(最左前缀原则)
核心:复合索引要从索引定义的最左侧字段开始,匹配连续前缀;SQL 的 where 条件书写顺序不影响索引命中,优化器会自动调整条件顺序。
✅ 可以有效使用索引前缀:
where a=1
where a=1 and b=2
where a=1 and b=2 and c=3
where b=2 and a=1 — where 条件顺序打乱,优化器重排,依旧命中索引
❌ 无法使用索引(缺失最左前缀):
where b=2
where b=2 and c=3
where c=5
误区:不是 where 子句写了 a/b/c 字段就一定能用索引,必须要有索引定义的最左起始列。
重点:遇到范围查询,后面字段无法做索引 seek
> < >= <= between 属于范围条件。一旦复合索引匹配中遇到范围查询,范围之后的字段不能再利用索引有序性做快速定位(seek)。
where a=10 and b>20 and c=5
- a:等值匹配,索引 seek(B+ 树二分定位)
- b:范围条件,索引 range 扫描
- c:不能走索引 seek。因为 b 范围之后,c 在 B+ 树叶子节点内部是无序的

上图完整演示了 where a=10 and b>20 and c=5 的执行过程:a 先等值 seek 定位;b 范围扫描命中连续 6 行;但这 6 行里的 c 值(5,12,5,3,40,5)完全无序,无法 seek 定位,只能逐行判断。
但是!c 不是完全失效,它可以交给索引下推 ICP 在二级索引页内过滤,这点是很多博客遗漏的关键点。
补充:in 算不算范围?
in(1,2,3) 不属于破坏索引的范围条件,MySQL 内部等价多个 or 等值,不会打断后面索引字段匹配,后续字段依旧可以 seek。
补充:MySQL 8.0 索引跳跃扫描(Skip Scan)
MySQL 8.0.13+ 引入 Skip Scan 优化。当缺失最左前缀时(如 where b=2 and c=3),优化器可能通过扫描 a 的所有不同值,在每个 a 值下分别做 b,c 的 seek 来利用索引,代价是扫描次数 = a 的 distinct 数量。Explain 中 type=range + Using index for skip scan。
二、索引下推 ICP(Index Condition Pushdown)
ICP 全称:索引条件下推,MySQL 5.6 之后支持,Explain Extra 字段显示 Using index condition。
没有 ICP 时代执行流程
BufferPool / 磁盘
存储引擎 InnoDB
MySQL Server 层
BufferPool / 磁盘
存储引擎 InnoDB
MySQL Server 层
#mermaid-svg-Zu1U4ObAzcPFy7j0{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-Zu1U4ObAzcPFy7j0 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Zu1U4ObAzcPFy7j0 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Zu1U4ObAzcPFy7j0 .error-icon{fill:#552222;}#mermaid-svg-Zu1U4ObAzcPFy7j0 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Zu1U4ObAzcPFy7j0 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Zu1U4ObAzcPFy7j0 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Zu1U4ObAzcPFy7j0 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Zu1U4ObAzcPFy7j0 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Zu1U4ObAzcPFy7j0 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Zu1U4ObAzcPFy7j0 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Zu1U4ObAzcPFy7j0 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Zu1U4ObAzcPFy7j0 .marker.cross{stroke:#333333;}#mermaid-svg-Zu1U4ObAzcPFy7j0 svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Zu1U4ObAzcPFy7j0 p{margin:0;}#mermaid-svg-Zu1U4ObAzcPFy7j0 .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-Zu1U4ObAzcPFy7j0 text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-Zu1U4ObAzcPFy7j0 .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-Zu1U4ObAzcPFy7j0 .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-Zu1U4ObAzcPFy7j0 .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-Zu1U4ObAzcPFy7j0 .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-Zu1U4ObAzcPFy7j0 #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-Zu1U4ObAzcPFy7j0 .sequenceNumber{fill:white;}#mermaid-svg-Zu1U4ObAzcPFy7j0 #sequencenumber{fill:#333;}#mermaid-svg-Zu1U4ObAzcPFy7j0 #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-Zu1U4ObAzcPFy7j0 .messageText{fill:#333;stroke:none;}#mermaid-svg-Zu1U4ObAzcPFy7j0 .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-Zu1U4ObAzcPFy7j0 .labelText,#mermaid-svg-Zu1U4ObAzcPFy7j0 .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-Zu1U4ObAzcPFy7j0 .loopText,#mermaid-svg-Zu1U4ObAzcPFy7j0 .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-Zu1U4ObAzcPFy7j0 .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-Zu1U4ObAzcPFy7j0 .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-Zu1U4ObAzcPFy7j0 .noteText,#mermaid-svg-Zu1U4ObAzcPFy7j0 .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-Zu1U4ObAzcPFy7j0 .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-Zu1U4ObAzcPFy7j0 .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-Zu1U4ObAzcPFy7j0 .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-Zu1U4ObAzcPFy7j0 .actorPopupMenu{position:absolute;}#mermaid-svg-Zu1U4ObAzcPFy7j0 .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-Zu1U4ObAzcPFy7j0 .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-Zu1U4ObAzcPFy7j0 .actor-man circle,#mermaid-svg-Zu1U4ObAzcPFy7j0 line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-Zu1U4ObAzcPFy7j0 :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
根据 a、b 条件扫描二级索引,拿到主键 id 集合
1
逐个主键回表查聚簇索引完整行
2
返回整行数据
3
返回全部行数据
4
在 Server 层过滤 c=5 条件,丢弃不满足的数据
5
问题:很多主键对应的行本来就不满足 c 条件,白白执行大量回表随机 IO。
开启 ICP 后流程
BufferPool / 磁盘
存储引擎 InnoDB
MySQL Server 层
BufferPool / 磁盘
存储引擎 InnoDB
MySQL Server 层
#mermaid-svg-wnKEUkPR2XmClVRy{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-wnKEUkPR2XmClVRy .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-wnKEUkPR2XmClVRy .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-wnKEUkPR2XmClVRy .error-icon{fill:#552222;}#mermaid-svg-wnKEUkPR2XmClVRy .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-wnKEUkPR2XmClVRy .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-wnKEUkPR2XmClVRy .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-wnKEUkPR2XmClVRy .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-wnKEUkPR2XmClVRy .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-wnKEUkPR2XmClVRy .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-wnKEUkPR2XmClVRy .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-wnKEUkPR2XmClVRy .marker{fill:#333333;stroke:#333333;}#mermaid-svg-wnKEUkPR2XmClVRy .marker.cross{stroke:#333333;}#mermaid-svg-wnKEUkPR2XmClVRy svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-wnKEUkPR2XmClVRy p{margin:0;}#mermaid-svg-wnKEUkPR2XmClVRy .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-wnKEUkPR2XmClVRy text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-wnKEUkPR2XmClVRy .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-wnKEUkPR2XmClVRy .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-wnKEUkPR2XmClVRy .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-wnKEUkPR2XmClVRy .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-wnKEUkPR2XmClVRy #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-wnKEUkPR2XmClVRy .sequenceNumber{fill:white;}#mermaid-svg-wnKEUkPR2XmClVRy #sequencenumber{fill:#333;}#mermaid-svg-wnKEUkPR2XmClVRy #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-wnKEUkPR2XmClVRy .messageText{fill:#333;stroke:none;}#mermaid-svg-wnKEUkPR2XmClVRy .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-wnKEUkPR2XmClVRy .labelText,#mermaid-svg-wnKEUkPR2XmClVRy .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-wnKEUkPR2XmClVRy .loopText,#mermaid-svg-wnKEUkPR2XmClVRy .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-wnKEUkPR2XmClVRy .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-wnKEUkPR2XmClVRy .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-wnKEUkPR2XmClVRy .noteText,#mermaid-svg-wnKEUkPR2XmClVRy .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-wnKEUkPR2XmClVRy .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-wnKEUkPR2XmClVRy .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-wnKEUkPR2XmClVRy .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-wnKEUkPR2XmClVRy .actorPopupMenu{position:absolute;}#mermaid-svg-wnKEUkPR2XmClVRy .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-wnKEUkPR2XmClVRy .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-wnKEUkPR2XmClVRy .actor-man circle,#mermaid-svg-wnKEUkPR2XmClVRy line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-wnKEUkPR2XmClVRy :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
根据 a seek,b 做 range 扫描
1
在二级索引页内直接利用 c 列过滤,不满足直接丢弃
2
只把过滤后剩余的主键 id 回表查整行
3
返回整行数据
4
返回过滤后的行
5
✔ ICP 本质:在二级索引页过滤数据,减少需要回表的主键数量,从而减少回表 IO。 ⚠ 注意:ICP 只是过滤,不能对 c 做索引 seek,不能利用 c 的有序性快速定位区间。

上图对比了有无 ICP 的核心差异:无 ICP 时 6 行全部回表,到 Server 层才发现 3 行不满足 c=5,回表白做;开启 ICP 后,存储引擎在索引页内先过滤 c=5,只剩 3 次回表,随机 IO 直接减半。
覆盖索引与 ICP 区分
Explain Extra:
- Using index:覆盖索引,直接从二级索引拿到全部查询字段,完全不需要回表。
- Using index condition:ICP,部分条件索引层过滤,仍然需要回表。
三、回表是什么?聚簇索引 vs 二级索引
InnoDB 中:
回表:拿到二级索引叶子的主键 id,再去主键 B+ 树查找完整行数据的过程。
如果查询需要的全部字段都在二级索引内,不需要读取完整行,就是覆盖索引,避免回表。
— 覆盖索引,Extra: Using index,无需回表
select a,b,c from test where a=10 and b>20;
key_len:判断复合索引用到多少字段
key_len 表示实际用到索引的字节长度。以 a INT, b INT, c INT 为例:
| where a=10 | 5 | 只用 a(INT 4 字节 + nullable 1 字节) |
| where a=10 and b>20 | 10 | 用到 a、b(b 范围仍计入 key_len) |
| where a=10 and b=2 and c=5 | 15 | 用到 a、b、c 全部 |
面试技巧:看到 key_len=10 就知道只用到前两个字段;key_len=5 说明只走了 a,b 没参与索引定位。
四、表空间与数据页组织:页从哪里来?
在讲 IO 类型之前,必须先回答一个问题:回表时访问的"页",在磁盘上是怎么存的?
InnoDB 的数据最终持久化在表空间(tablespace)中。开启 innodb_file_per_table=ON 时,每张表对应一个独立的 .ibd 文件,这就是该表的独立表空间。
表空间内部是层级组织:
| 页(Page) | 16KB | 最小读写单位,存实际数据行 |
| 区(Extent) | 1MB = 64 页 | 空间分配的最小单位,保证区内页物理连续 |
| 段(Segment) | 变长 | 逻辑概念,如叶子节点段、非叶子节点段 |
因果链:
因为 表空间按 extent 成片分配(一次分 64 页,物理连续)
→ 所以 同一段时期内分配的页,物理位置大概率相邻
→ 但因为 增删改导致页分裂、合并、回收再分配
→ 所以 逻辑上页号相邻的两页,磁盘位置可能已经分散
→ 因此 判定顺序/随机 IO 不能看物理位置,只能看页号访问次序
关键认知:B+ 树叶子节点的"逻辑有序"≠"物理有序"。表空间决定了页的物理落脚处,而页号只是表空间内的逻辑编号。理解这一点,才能真正理解下一节的"顺序/随机 IO"。
五、顺序 IO、随机 IO,不要再记死口诀!
网上流传:二级索引扫描 = 顺序 IO,回表 = 随机 IO。这句话只是绝大多数场景的经验总结,不是铁律定义。
InnoDB 判定顺序/随机访问模式
InnoDB 看不到磁盘物理扇区,看的是表空间页号的访问序列:
关键点:顺序 IO、随机 IO 是访问模式,不是索引自带属性。
示例 1:聚簇索引(主键)
— 主键连续读取,页号递增,顺序 IO
select * from test where id between 1000 and 2000;
— 主键乱序跳跃读取,页号到处跳,随机 IO
select * from test where id in (1001, 7, 3900, 56);
普通回表为什么大多是随机 IO:
二级索引筛选出来的主键 id 集合,排序规则跟随 (a,b,c),主键 id 是乱序打散的;拿着一堆无序 id 访问聚簇索引,页号来回跳,产生随机 IO。
MRR 优化:把回表随机 IO 转为顺序 IO
MRR(Multi-Range Read)多范围读优化,MySQL 官方专门解决回表大量随机 IO 的方案:
Explain 会看到 Extra:Using MRR。MRR 充分证明,回表本身不等于随机 IO,访问主键的次序决定 IO 类型。

上图演示了 MRR 的核心机制:二级索引按 (a,b,c) 序吐出主键 57,3,812,21,406(乱序),直接回表时页号来回跳 = 随机 IO;MRR 先在缓冲区排成升序 3,21,57,406,812,再按页号递增顺序访问 → 顺序 IO + 触发预读。
重要结论
如果需要访问的数据页全部命中 Buffer Pool 内存,不存在磁盘 IO,顺序 IO、随机 IO 没有性能差异。顺序/随机 IO 概念只针对磁盘访问场景。
六、延伸理解:BufferPool、脏页,帮你看懂 SQL 底层 IO 行为
1. BufferPool LRU 冷热分区
#mermaid-svg-aPSAqH6ZIZs0Xbke{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-aPSAqH6ZIZs0Xbke .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-aPSAqH6ZIZs0Xbke .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-aPSAqH6ZIZs0Xbke .error-icon{fill:#552222;}#mermaid-svg-aPSAqH6ZIZs0Xbke .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-aPSAqH6ZIZs0Xbke .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-aPSAqH6ZIZs0Xbke .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-aPSAqH6ZIZs0Xbke .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-aPSAqH6ZIZs0Xbke .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-aPSAqH6ZIZs0Xbke .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-aPSAqH6ZIZs0Xbke .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-aPSAqH6ZIZs0Xbke .marker{fill:#333333;stroke:#333333;}#mermaid-svg-aPSAqH6ZIZs0Xbke .marker.cross{stroke:#333333;}#mermaid-svg-aPSAqH6ZIZs0Xbke svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-aPSAqH6ZIZs0Xbke p{margin:0;}#mermaid-svg-aPSAqH6ZIZs0Xbke .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-aPSAqH6ZIZs0Xbke .cluster-label text{fill:#333;}#mermaid-svg-aPSAqH6ZIZs0Xbke .cluster-label span{color:#333;}#mermaid-svg-aPSAqH6ZIZs0Xbke .cluster-label span p{background-color:transparent;}#mermaid-svg-aPSAqH6ZIZs0Xbke .label text,#mermaid-svg-aPSAqH6ZIZs0Xbke span{fill:#333;color:#333;}#mermaid-svg-aPSAqH6ZIZs0Xbke .node rect,#mermaid-svg-aPSAqH6ZIZs0Xbke .node circle,#mermaid-svg-aPSAqH6ZIZs0Xbke .node ellipse,#mermaid-svg-aPSAqH6ZIZs0Xbke .node polygon,#mermaid-svg-aPSAqH6ZIZs0Xbke .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-aPSAqH6ZIZs0Xbke .rough-node .label text,#mermaid-svg-aPSAqH6ZIZs0Xbke .node .label text,#mermaid-svg-aPSAqH6ZIZs0Xbke .image-shape .label,#mermaid-svg-aPSAqH6ZIZs0Xbke .icon-shape .label{text-anchor:middle;}#mermaid-svg-aPSAqH6ZIZs0Xbke .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-aPSAqH6ZIZs0Xbke .rough-node .label,#mermaid-svg-aPSAqH6ZIZs0Xbke .node .label,#mermaid-svg-aPSAqH6ZIZs0Xbke .image-shape .label,#mermaid-svg-aPSAqH6ZIZs0Xbke .icon-shape .label{text-align:center;}#mermaid-svg-aPSAqH6ZIZs0Xbke .node.clickable{cursor:pointer;}#mermaid-svg-aPSAqH6ZIZs0Xbke .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-aPSAqH6ZIZs0Xbke .arrowheadPath{fill:#333333;}#mermaid-svg-aPSAqH6ZIZs0Xbke .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-aPSAqH6ZIZs0Xbke .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-aPSAqH6ZIZs0Xbke .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-aPSAqH6ZIZs0Xbke .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-aPSAqH6ZIZs0Xbke .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-aPSAqH6ZIZs0Xbke .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-aPSAqH6ZIZs0Xbke .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-aPSAqH6ZIZs0Xbke .cluster text{fill:#333;}#mermaid-svg-aPSAqH6ZIZs0Xbke .cluster span{color:#333;}#mermaid-svg-aPSAqH6ZIZs0Xbke div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-aPSAqH6ZIZs0Xbke .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-aPSAqH6ZIZs0Xbke rect.text{fill:none;stroke-width:0;}#mermaid-svg-aPSAqH6ZIZs0Xbke .icon-shape,#mermaid-svg-aPSAqH6ZIZs0Xbke .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-aPSAqH6ZIZs0Xbke .icon-shape p,#mermaid-svg-aPSAqH6ZIZs0Xbke .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-aPSAqH6ZIZs0Xbke .icon-shape .label rect,#mermaid-svg-aPSAqH6ZIZs0Xbke .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-aPSAqH6ZIZs0Xbke .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-aPSAqH6ZIZs0Xbke .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-aPSAqH6ZIZs0Xbke :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
BufferPool
否
是
young 热区(最近频繁访问)
old 冷区(新页默认进来)
新页加载
1s 内再次访问?
停留 >1s 再访问
淘汰冷页
淘汰热页
全表扫描大量新页
InnoDB 使用改良 LRU 链表,分为 young 热区、old 冷区。新页默认进入 old 区头部;只有在 old 区停留超过 innodb_old_blocks_time(默认 1000ms)后再次被访问,才会移到 young 区头部。这避免了全表扫描一次性冲掉全部热点缓存。
2. 脏页生命周期
#mermaid-svg-vJbTcqFybr50Rk5b{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-vJbTcqFybr50Rk5b .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-vJbTcqFybr50Rk5b .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-vJbTcqFybr50Rk5b .error-icon{fill:#552222;}#mermaid-svg-vJbTcqFybr50Rk5b .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-vJbTcqFybr50Rk5b .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-vJbTcqFybr50Rk5b .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-vJbTcqFybr50Rk5b .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-vJbTcqFybr50Rk5b .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-vJbTcqFybr50Rk5b .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-vJbTcqFybr50Rk5b .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-vJbTcqFybr50Rk5b .marker{fill:#333333;stroke:#333333;}#mermaid-svg-vJbTcqFybr50Rk5b .marker.cross{stroke:#333333;}#mermaid-svg-vJbTcqFybr50Rk5b svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-vJbTcqFybr50Rk5b p{margin:0;}#mermaid-svg-vJbTcqFybr50Rk5b defs #statediagram-barbEnd{fill:#333333;stroke:#333333;}#mermaid-svg-vJbTcqFybr50Rk5b g.stateGroup text{fill:#9370DB;stroke:none;font-size:10px;}#mermaid-svg-vJbTcqFybr50Rk5b g.stateGroup text{fill:#333;stroke:none;font-size:10px;}#mermaid-svg-vJbTcqFybr50Rk5b g.stateGroup .state-title{font-weight:bolder;fill:#131300;}#mermaid-svg-vJbTcqFybr50Rk5b g.stateGroup rect{fill:#ECECFF;stroke:#9370DB;}#mermaid-svg-vJbTcqFybr50Rk5b g.stateGroup line{stroke:#333333;stroke-width:1;}#mermaid-svg-vJbTcqFybr50Rk5b .transition{stroke:#333333;stroke-width:1;fill:none;}#mermaid-svg-vJbTcqFybr50Rk5b .stateGroup .composit{fill:white;border-bottom:1px;}#mermaid-svg-vJbTcqFybr50Rk5b .stateGroup .alt-composit{fill:#e0e0e0;border-bottom:1px;}#mermaid-svg-vJbTcqFybr50Rk5b .state-note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-vJbTcqFybr50Rk5b .state-note text{fill:black;stroke:none;font-size:10px;}#mermaid-svg-vJbTcqFybr50Rk5b .stateLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5;}#mermaid-svg-vJbTcqFybr50Rk5b .edgeLabel .label rect{fill:#ECECFF;opacity:0.5;}#mermaid-svg-vJbTcqFybr50Rk5b .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-vJbTcqFybr50Rk5b .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-vJbTcqFybr50Rk5b .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-vJbTcqFybr50Rk5b .edgeLabel .label text{fill:#333;}#mermaid-svg-vJbTcqFybr50Rk5b .label div .edgeLabel{color:#333;}#mermaid-svg-vJbTcqFybr50Rk5b .stateLabel text{fill:#131300;font-size:10px;font-weight:bold;}#mermaid-svg-vJbTcqFybr50Rk5b .node circle.state-start{fill:#333333;stroke:#333333;}#mermaid-svg-vJbTcqFybr50Rk5b .node .fork-join{fill:#333333;stroke:#333333;}#mermaid-svg-vJbTcqFybr50Rk5b .node circle.state-end{fill:#9370DB;stroke:white;stroke-width:1.5;}#mermaid-svg-vJbTcqFybr50Rk5b .end-state-inner{fill:white;stroke-width:1.5;}#mermaid-svg-vJbTcqFybr50Rk5b .node rect{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-vJbTcqFybr50Rk5b .node polygon{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-vJbTcqFybr50Rk5b #statediagram-barbEnd{fill:#333333;}#mermaid-svg-vJbTcqFybr50Rk5b .statediagram-cluster rect{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-vJbTcqFybr50Rk5b .cluster-label,#mermaid-svg-vJbTcqFybr50Rk5b .nodeLabel{color:#131300;}#mermaid-svg-vJbTcqFybr50Rk5b .statediagram-cluster rect.outer{rx:5px;ry:5px;}#mermaid-svg-vJbTcqFybr50Rk5b .statediagram-state .divider{stroke:#9370DB;}#mermaid-svg-vJbTcqFybr50Rk5b .statediagram-state .title-state{rx:5px;ry:5px;}#mermaid-svg-vJbTcqFybr50Rk5b .statediagram-cluster.statediagram-cluster .inner{fill:white;}#mermaid-svg-vJbTcqFybr50Rk5b .statediagram-cluster.statediagram-cluster-alt .inner{fill:#f0f0f0;}#mermaid-svg-vJbTcqFybr50Rk5b .statediagram-cluster .inner{rx:0;ry:0;}#mermaid-svg-vJbTcqFybr50Rk5b .statediagram-state rect.basic{rx:5px;ry:5px;}#mermaid-svg-vJbTcqFybr50Rk5b .statediagram-state rect.divider{stroke-dasharray:10,10;fill:#f0f0f0;}#mermaid-svg-vJbTcqFybr50Rk5b .note-edge{stroke-dasharray:5;}#mermaid-svg-vJbTcqFybr50Rk5b .statediagram-note rect{fill:#fff5ad;stroke:#aaaa33;stroke-width:1px;rx:0;ry:0;}#mermaid-svg-vJbTcqFybr50Rk5b .statediagram-note rect{fill:#fff5ad;stroke:#aaaa33;stroke-width:1px;rx:0;ry:0;}#mermaid-svg-vJbTcqFybr50Rk5b .statediagram-note text{fill:black;}#mermaid-svg-vJbTcqFybr50Rk5b .statediagram-note .nodeLabel{color:black;}#mermaid-svg-vJbTcqFybr50Rk5b .statediagram .edgeLabel{color:red;}#mermaid-svg-vJbTcqFybr50Rk5b #dependencyStart,#mermaid-svg-vJbTcqFybr50Rk5b #dependencyEnd{fill:#333333;stroke:#333333;stroke-width:1;}#mermaid-svg-vJbTcqFybr50Rk5b .statediagramTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-vJbTcqFybr50Rk5b :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
从磁盘加载到 BufferPool
UPDATE/INSERT/DELETE 修改数据
Page Cleaner 异步刷脏成功
LRU 淘汰,直接丢弃
LRU 淘汰,先刷脏再释放
干净页
脏页
3. 脏页是否可读?
脏页完全可以对外查询。
脏页定义:Buffer Pool 内存页被修改,内存版本 > 磁盘持久化版本;磁盘存旧数据。
查询优先读取 Buffer Pool 内存中的页,不管它是不是脏页;后台 Page Cleaner 线程异步刷脏页,刷脏不会阻塞读写,刷盘成功脏页变成干净页,该页依旧留在 BufferPool。
4. 脏页刷盘成功后,为什么不直接删除,要用 LRU 淘汰?
很多人误区:脏页落盘完毕就没用了,直接清掉。
- 淘汰脏页:先刷脏页落盘,再释放内存;
- 淘汰干净页:直接丢弃,磁盘已有副本。
七、Explain 关键字段回顾(做索引分析必看)
| type | 访问类型 | ref=等值索引查找;range=范围索引扫描;ALL=全表扫描 |
| key | 实际使用索引 | 确认是否走了预期索引 |
| key_len | 实际用到索引字节长度 | 判断复合索引用到多少字段(INT nullable = 5 字节) |
| Extra | 额外信息 | Using index=覆盖索引无回表;Using index condition=ICP;Using MRR=多范围读优化;Using filesort=需额外排序 |
八、高频踩坑总结(面试速记)
九、因果链总收束:一条链串起所有概念
因为 复合索引叶子按 (a,b,c) 全局有序
→ 所以 查询可以从 a 开始二分 seek
→ 因为 排序优先级 a→b→c 依次递减
→ 所以 b 范围后 c 在叶子内部无序
→ 因为 c 无序无法 seek,只能逐行判断
→ 所以 引入 ICP 在引擎层过滤,减少回表
→ 因为 二级索引吐出主键 id 跟随 (a,b,c) 排序,主键乱序
→ 所以 回表需要访问聚簇索引页
→ 因为 聚簇索引页存储在表空间中,页号由表空间分配
→ 所以 回表默认是随机 IO(页号跳跃)
→ 因为 MRR 把主键排序后再访问
→ 所以 回表变成顺序 IO + 触发预读
→ 因为 页全部命中 BufferPool 时不存在磁盘 IO
→ 所以 顺序/随机 IO 概念只在磁盘层有意义
这条链上的每一个环节,都是前一个环节的必然推论。拿掉任何一节,后面的结论都不成立。
参考资料
- 《高性能 MySQL》第 5 章 — 索引设计
- MySQL 官方文档 — Index Condition Pushdown
- MySQL 官方文档 — Multi-Range Read Optimization
- MySQL 官方文档 — InnoDB Buffer Pool
- MySQL 官方文档 — InnoDB Read-Ahead
- MySQL 索引下推 ICP 详解 — 博客园
- MySQL MRR 优化详解 — 博客园
- InnoDB Buffer Pool LRU 冷热分区 — CSDN
网硕互联帮助中心




评论前必须登录!
注册