mysql优化器基于成本模型决策是否启用mrr:对比传统随机回表与mrr排序+顺序回表的i/o和cpu开销,仅当mrr路径总成本低于传统路径且节省超mrr_cost_threshold(默认10.0)时才启用。

MySQL优化器怎么算MRR值不值得用
MySQL不会盲目启用MRR,它在执行计划生成阶段就完成了一套成本估算:对比“传统随机回表”和“MRR排序+顺序回表”两种路径的I/O与CPU开销。这个决策由优化器内部的cost model驱动,不是简单开关控制。
-
mrr_cost_based=on(默认开启)时,优化器会计算两个关键成本项:
• 传统方式的rnd_next调用次数 × 单次随机读平均代价(含寻道、旋转延迟)
• MRR方式的read_rnd_buffer_size内存排序开销 +rnd_pos顺序读次数 × 单次顺序读代价(远低于随机读) - 若MRR路径总成本比传统路径低超过
mrr_cost_threshold(默认10.0),才真正启用 - 这个阈值是相对值,不是绝对行数;哪怕只省5%的I/O,但CPU排序代价太高,也可能被否决
你看到EXPLAIN里没出现Using MRR,大概率是成本模型判它“不划算”,而不是配置没开。
哪些参数直接影响MRR成本估算结果
优化器做判断时,依赖几个可调参数的真实值,它们不是“设了就生效”,而是直接参与公式计算:
-
read_rnd_buffer_size:缓冲区越大,越可能攒够一批ID再排序,减少分批次数;但太大时,排序本身耗CPU,反而拉高MRR路径成本 -
mrr_cost_threshold:不是“启用门槛”,而是“收益放大器”。设为5.0不代表5行就触发,而是要求MRR节省的成本必须达到传统路径的5倍以上才采纳 - 表统计信息(
cardinality、avg_row_length等):影响优化器对回表行数、主键离散度、页分裂程度的预估,这些全参与I/O成本建模
比如read_rnd_buffer_size从256KB调到4MB后,EXPLAIN FORMAT=JSON里"mrr_cost"字段数值可能从8.2跳到12.7——说明成本模型突然觉得MRR划算了。
为什么EXPLAIN显示Using MRR,但实际性能没变好
这是最常被忽略的矛盾点:MRR确实启用了,但没带来预期加速,甚至更慢。
- 缓冲区太小导致频繁flush:比如
read_rnd_buffer_size仅256KB,而单条索引记录带主键+二级索引字段共200字节,最多缓存1280个ID;若查询返回5000行,就得分4批排序+回表,额外开销抵消了顺序IO收益 - 主键物理聚集度高:数据按主键递增插入,且未大量删除/更新,此时原始二级索引查出的主键本身就接近有序,MRR排序带来的顺序性提升微乎其微
- 存储引擎层干扰:InnoDB的adaptive hash index或change buffer可能已把热点页缓存在内存,随机读实际走的是内存而非磁盘,MRR的I/O优势无法体现
这种情况下,sys.schema_table_statistics里rnd_next下降但rnd_pos上升,且总执行时间不变,就是典型的“MRR生效但无收益”。
如何验证MRR是否真在降低随机IO
别只信EXPLAIN的Extra字段,它可能被截断或滞后。真实效果要看存储引擎暴露的计数器:
- 执行查询前后,对比
SHOW STATUS LIKE 'Handler_read%':
•Handler_read_rnd(随机读行)应明显下降
•Handler_read_rnd_next(随机读下一行)才是MRR真正替代的对象,下降越显著,说明优化越到位 - 更精准的方式是查
sys.schema_table_statistics:
•rnd_next列代表传统随机回表次数
•rnd_pos列代表MRR启用后的顺序定位次数
• 两者差值越大,MRR介入越深
注意:rnd_pos不为0,但rnd_next没降,说明MRR只是“参与了流程”,但没真正接管回表逻辑——往往因为read_rnd_buffer_size太小或mrr_cost_threshold设得过高。











