惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Google DeepMind News
Google DeepMind News
F
Fortinet All Blogs
量子位
G
Google Developers Blog
J
Java Code Geeks
N
Netflix TechBlog - Medium
博客园 - 聂微东
宝玉的分享
宝玉的分享
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
月光博客
月光博客
The Cloudflare Blog
Apple Machine Learning Research
Apple Machine Learning Research
爱范儿
爱范儿
雷峰网
雷峰网
M
MIT News - Artificial intelligence
T
Tailwind CSS Blog
V
Visual Studio Blog
阮一峰的网络日志
阮一峰的网络日志
博客园 - 三生石上(FineUI控件)
Microsoft Azure Blog
Microsoft Azure Blog
aimingoo的专栏
aimingoo的专栏
Martin Fowler
Martin Fowler
有赞技术团队
有赞技术团队
T
The Blog of Author Tim Ferriss

博客园 - 幽州散人

SQL优化案例 synchronized 为啥会让虚拟线程卸载失效 主线任务和使命 温州3日之旅 CompletableFuture + 异步Servlet:同步的姿势、异步的效果 Snowflake雪花算法与发号器的应用 windows Java 幽灵转发壳进程 也谈MySQL limit offset深翻页问题 SHOW SESSION STATUS 与 Handler 变量 MySQL 5.7 Profiles 执行性能分析 MySQL临时表与文件排序 MySQL 5.7 ICP索引条件下推优化 MySQL符合索引与最左前缀原则 从I/O 的物理成本理解回表和索引覆盖 第4个电瓶以及补漆 Flink原理:并行度与keyGroup桶 什么是数字批发银行 Java虚拟线程(三)实现原理 Java虚拟线程(二) Java虚拟线程(一) 大模型推理层服务化架构 传奇调查员之路:理智值-99,但我必须听懂深渊的语言 js里调用智能合约读/写函数的方法的区别 字节与其16进制字符表示转换的bug Redisson分布式锁 交易心得 DexScreener接口初探 某安全软件跑飞了。。 Ed25519算法签名与验签的Java实现 Tendermint拜占庭容错引擎
MySQL 5.7 MRR多范围读优化
幽州散人 · 2026-08-13 · via 博客园 - 幽州散人

MRR (Multi-Range Read,多范围读) 是MySQL 5.7 默认启用的一个优化策略。

原理

Server 层执行器要数据,得通过存储引擎 API。在没有 MRR 的时候,执行器拿回表数据的方式是一次要一条:

没有 MRR 的调用过程:

Server: 引擎,把主键=12 的行给我
InnoDB: (去聚集索引读一次 → 返回)
Server: 引擎,把主键=58 的行给我
InnoDB: (再读一次 → 返回)
Server: 引擎,把主键=3291 的行给我
InnoDB: (再读一次 → 返回)
...重复 500 次...

每次调用引擎只能看到"一条主键",
它没法提前知道下一个要读什么 → 无法规划 → 只能来一条读一条(乱序随机)

有了 MRR,API 变成了一次给一批:

有 MRR 的调用过程:

Server: 引擎,主键在 [12, 58, 3291, 8812, ...] 这个"多范围"集合里的行,一次都给我
InnoDB: (看到完整批次了!)
        → 我先排序 → 12, 58, 3291, 8812
        → 我再按页合并 → P1(12,58), P6(3291), P34(8812)
        → 按页顺序批量读 → 一起返回

即:

没有 MRR:
    通过二级索引找到多个主键值 → 按主键值出现的顺序随机回表 I/O

有 MRR:
    通过二级索引找到多个主键值 → 先排序主键 → 按主键顺序批量回表
    → 磁盘随机 I/O 变顺序 I/O → 更快

收益好处

MRR 带来的收益是真实的,收益来自三个方面:

  1. 单调递增的页访问 ≠ 乱序跳转

    聚集索引的叶子页在磁盘上按主键顺序排列(B+Tree 的本质):

      页P1       页P2       页P3    ...    页P20
    [id=1~100][id=101~200][id=201~300]  [id=1901~2000]
         ↑
      PK=12, 580, 3291, 8812 分别落在 P1, P6, P34, P89
    

    没有 MRR(按二级索引扫出来的顺序回表):
    扫二级索引得到:8812, 12, 3291, 580, ...
    回表顺序:P89 → P1 → P34 → P6 → ...
    磁盘磁头:跳到89 → 跳回1 → 跳到34 → 跳回6
    HDD 每次跨页寻道 5-10ms,来回横跳

    有 MRR(先排序再回表):
    排序后:12, 580, 3291, 8812
    回表顺序:P1 → P6 → P34 → P89
    磁头朝一个方向走,每次只跨几个磁道
    → 不是严格连续,但接近顺序扫的代价

    HDD 上"单调前进"和"来回横跳"的差距就是好几倍,这跟电梯类比:从 1 楼 → 6 楼 → 34 楼 → 89 楼往上走,比 89 → 1 → 34 → 6 来回折返快得多。

  2. 同一页内的多条主键,合并成一次读取

    这是 MRR 收益最大的一块:

    二级索引扫出 500 个主键,其中 30 个落在 P1(id=1~100)
    没有 MRR:
    回表读 id=12 时读一次 P1
    过一会回表读 id=58 时,P1 可能已被挤出 Buffer Pool → 再读一次 P1
    最坏情况:同一个页被读了 30 次

    有 MRR:
    排序后,P1 里的 30 个主键集中处理
    读一次 P1,把 30 行全取出来
    → 500 次回表可能压缩成 80 次页读取

  3. 预读(read-ahead)友好

    磁盘/OS 层都有预读机制:你读了 P6,它顺手把 P7、P8 也捞进缓存。乱序访问时预读的页用不上就浪费了;单调递增访问时预读的页大概率马上用到。


总结

▎ MRR 把"乱序回表"变成"按主键单调递增的页访问",虽然不是物理连续的顺序 I/O,但实现了:
▎ ① 磁头单向移动(减少寻道) ② 同页主键合并读取(减少重复读页) ③ 预读友好。

▎ 本质是降低随机性,而不是达到严格顺序。

另外 :MySQL 5.7 里 MRR 默认开着,但 mrr_cost_based=on 意味着优化器会按成本决定用不用;在 HDD 上收益明显,在 SSD 上收益小但仍有效(因为减少了 I/O 请求次数)。