MRR仅在“二级索引范围扫描+必须回表+主键物理离散”三者同时满足时由优化器自动启用,典型触发查询包括WHERE status IN (1,2,3)或BETWEEN范围查询;不生效于覆盖索引、主键扫描及函数索引等场景。

MRR 不是开关,它只在“二级索引范围扫描 + 必须回表 + 主键物理离散”三者同时满足时,由优化器自动启用;盲目调参或强开反而拖慢查询。
哪些查询实际触发 MRR?
MRR 生效的前提非常具体,不是所有带 WHERE 的查询都能用上:
-
WHERE status IN (1,2,3):二级索引查出多个主键 ID,再回表取全行,且这些 ID 在聚簇索引中分布离散(比如跨多个页) -
WHERE created_at BETWEEN '2025-01-01' AND '2025-12-31':范围条件走二级索引,估算回表行数 > 10(默认阈值),且未被覆盖索引绕过 -
WHERE category = 'A' AND price > 100:联合索引最左匹配后带范围,无法跳过回表 - 不生效的典型场景:
SELECT id, name FROM t WHERE status = 1(覆盖索引)、SELECT * FROM t WHERE id > 1000(主键扫描)、WHERE JSON_CONTAINS(data, '"abc"')(函数索引不支持 MRR)
怎么确认 MRR 真正在跑?
仅看 EXPLAIN 的 Extra 列含 Using MRR 不够可靠——它可能被跳过、被成本模型否决,或因缓冲区太小而退化。必须组合验证:
- 执行
EXPLAIN FORMAT=JSON SELECT ...,检查输出中是否存在"using_mrr": true和"mrr_cost"字段(值为正数才表示启用) - 观察
Extra:出现Using index condition; Using MRR表示 ICP 和 MRR 同时生效;只有Using index condition没有Using MRR,大概率是read_rnd_buffer_size太小或预估行数低于阈值 - 查系统表:
SELECT * FROM sys.schema_table_statistics WHERE table_name = 'your_table',对比rnd_next(随机读次数)是否明显下降、rnd_pos(顺序读次数)是否上升
如何调整参数让 MRR 更稳定生效?
read_rnd_buffer_size 是关键会话级参数,但它不是越大越好:
- 设太小(如默认 256K):小结果集直接跳过 MRR,走传统随机回表
- 设太大(如 16M):单连接内存暴涨,高并发下易 OOM;若二级索引本身很宽(比如含
TEXT列),排序开销可能反超 IO 节省 - 推荐实操值:
SET SESSION read_rnd_buffer_size = 4194304(4M),改完必须在同 session 中跑真实查询或EXPLAIN才能验证效果 - 注意:
optimizer_switch中需保持mrr=on且mrr_cost_based=on(后者默认开启,表示启用成本评估;设为off可能强制启用但风险高)
为什么有时 MRR 效果不明显甚至变慢?
MRR 的收益高度依赖数据物理布局和访问模式,容易被忽略的点包括:
- 主键本身连续(比如自增 ID + 时间范围查询较窄):排序后回表仍是局部顺序,MRR 带来的 IO 改善有限
- 数据已在 InnoDB buffer pool 中缓存:随机读变成内存访问,MRR 的“减少磁盘随机 IO”优势消失
- 二级索引列太宽(例如含长字符串或 JSON 字段):收集、排序 key 的 CPU 开销可能抵消 IO 节省
-
ORDER BY强制保序(如ORDER BY id LIMIT 100):MySQL 可能放弃 MRR 以避免打乱原始索引顺序


















