不一定。using mrr仅表示优化器启用mrr,实际效果取决于数据分布与缓冲区配置;真正触发需同时满足走二级索引、需回表且预估回表行数超mrr_cost_threshold。

EXPLAIN里看到Using MRR就代表优化生效了吗
不一定。出现Using MRR只说明优化器决定启用MRR,但真实效果取决于数据分布和缓冲区配置。常见误判是把Using index condition当成MRR——那是索引下推(ICP),和MRR无关。真正触发MRR必须同时满足:走二级索引、需回表、且预估回表行数超过mrr_cost_threshold(默认10.0)。如果结果集太小(比如
read_rnd_buffer_size设多大才合适
这个值不是越大越好,它是每个连接独占的内存,设太高容易引发OOM。默认256KB在多数场景下偏小,会导致MRR分批太碎,失去批量优势。实操建议:
- 先用
SET SESSION read_rnd_buffer_size = 4194304(4MB)测试 - 观察
sys.schema_table_statistics中rnd_next(随机读)是否明显下降、rnd_pos(顺序读)是否上升 - 若二级索引本身很宽(比如含
TEXT或多个VARCHAR(1000)),排序开销可能反超IO节省,此时反而要调小 - 别全局设置,高并发下SESSION级调整更安全
MRR和覆盖索引谁该优先考虑
覆盖索引是消除回表,MRR是优化回表——两者不在同一层,也不冲突。真实优化路径很明确:
- 第一步:检查
SELECT字段能否被现有二级索引完全覆盖;能就加联合索引,让Extra变成Using index - 第二步:若必须查大字段(如
TEXT、BLOB)或字段组合太多,再看是否满足MRR条件(范围查询+二级索引+主键离散) - 第三步:确认
optimizer_switch中mrr=on且mrr_cost_based=on,再调read_rnd_buffer_size
强行给覆盖索引的查询开MRR毫无意义,因为根本不会回表。
哪些WHERE条件实际能触发MRR
MRR不是所有范围查询都管用,它只对“二级索引扫描 → 回表 → 主键物理分布离散”这一链条起作用。典型有效场景:
-
WHERE status IN (1,2,3)(IN列表匹配二级索引) -
WHERE created_at BETWEEN '2025-01-01' AND '2025-12-31'(时间范围+二级索引) -
WHERE category = 'A' AND price > 100(联合索引最左匹配后带范围)
无效场景包括:SELECT * FROM t WHERE id > 1000(主键扫描)、SELECT id,name FROM t WHERE status = 1(覆盖索引)、WHERE JSON_CONTAINS(data, '"abc"')(虚拟列索引不支持MRR)。
真正容易被忽略的是:即使开了MRR,如果主键本身就是自增且范围窄(比如id BETWEEN 1000 AND 1050),物理局部性好,顺序读优势就极小——这时MRR收益几乎为零。











