mysql中优化器的成本模型与执行计划选择

来源:这里教程网 时间:2026-02-28 20:42:23 作者:

MySQL优化器怎么估算“哪个执行计划更便宜”

MySQL优化器不靠猜,它有一套成本模型(Cost Model),把每个可能的执行计划换算成一个数字——

cost
,选
cost
最小的那个。这个
cost
不是时间,而是抽象的“资源开销单位”,主要由三部分构成:
IO_cost
(读页次数)、
CPU_cost
(行处理开销)、
memory_cost
(临时内存使用)。其中
IO_cost
通常占大头,尤其在磁盘表场景下。

关键点在于:这些成本值不是固定常量,而是依赖统计信息(如

INFORMATION_SCHEMA.STATISTICS
mysql.innodb_table_stats
)动态计算的。如果
ANALYZE TABLE
没跑过,或者数据剧烈变化后没更新统计信息,优化器就容易算错——比如低估了某索引的回表行数,结果选了
index_merge
而不是更优的
ref
扫描。

如何查看优化器实际用了哪些成本参数

MySQL 5.7+ 支持通过

optimizer_trace
看到每一步的成本估算细节。开启后执行查询,再查
information_schema.OPTIMIZER_TRACE
就能看到完整推演过程,包括各访问路径的
cost
rows
预估、是否用到索引等。

开启方式:
SET optimizer_trace="enabled=on,one_line=off";
执行目标SQL(比如
SELECT * FROM orders WHERE user_id = 123 AND status = 'paid';
查结果:
SELECT trace FROM information_schema.OPTIMIZER_TRACE;
重点关注
considered_execution_plans
里的
cost
字段和
rows
字段是否合理;若
rows
比实际结果大几个数量级,大概率是统计信息过期

为什么
FORCE INDEX
有时反而让查询变慢

强制索引会跳过成本计算,但优化器原本放弃该索引,往往是因为它算出来走这个索引的总

cost
更高——比如二级索引+回表的IO开销,大于全表扫描的顺序读开销(尤其当
WHERE
条件匹配度高、需要返回大量行时)。这时硬加
FORCE INDEX
,等于让优化器“闭眼执行”,可能触发大量随机IO。

典型误用场景:

对宽表(几十列)用
SELECT *
+
FORCE INDEX
查非覆盖索引 → 每行都要回主键查找,
IO_cost
爆炸
ORDER BY
字段上强制索引,但排序结果集很大,导致
sort_buffer
溢出写磁盘 →
memory_cost
失控
复合索引顺序和
WHERE
条件不匹配,强制后仍无法跳过范围扫描前缀 → 实际
rows
远超预估

影响成本模型判断的几个隐藏开关

除了统计信息,还有几个系统变量会直接修改成本权重,进而改变执行计划选择:

eq_range_index_dive_limit
:控制优化器对
IN
列表是否做索引下潜(index dive)统计。设为
0
会禁用下潜,改用采样估算,可能导致
IN (1,2,3,...,100)
被当成低选择性条件
range_optimizer_max_mem_size
:限制范围扫描时用于计算区间数的最大内存。太小会导致优化器放弃精确区间分析,退化为粗略估算
join_cache_level
:影响嵌套循环连接中是否启用块嵌套循环(BNL)或哈希连接,改变
CPU_cost
memory_cost
权衡
use_stat_tables
:决定是否使用持久化统计信息(
innodb_table_stats
),设为
preferably
且表有持久统计时,会绕过
ANALYZE TABLE
临时采样

这些变量不常调,但一旦线上出现“同一条SQL在不同实例上执行计划不同”,优先检查它们是否一致。成本模型本身不神秘,但它极度依赖输入质量——统计不准、配置偏移、数据分布突变,任何一个都可能让

cost
从参考值变成误导值。

相关推荐