MySQL的cost是CPU与IO加权估算值,无量纲且不可直接获取;需通过optimizer_trace查看各执行路径的估算cost,结合server_cost和engine_cost表系数及统计信息动态计算。

MySQL 不会直接告诉你某条查询的 cost 是 12.7 还是 89.3,它只在优化器内部用这个值做决策;你真正能拿到的,是 EXPLAIN 里的 rows 和 type,以及开启 optimizer_trace 后看到的各执行路径的估算 cost——这才是理解“为什么选这个索引”的唯一可靠入口。
cost 值不是真实耗时,而是 CPU + IO 的加权估算
MySQL 的 cost 是个无量纲数值,由两部分构成:cpu_cost(行比较开销)和 io_cost(页读取开销),两者简单相加。它不考虑当前磁盘负载、CPU 饱和度或缓存命中率,只依赖静态系数和统计信息。
-
cpu_cost主要基于预估扫描行数 ÷TIME_FOR_COMPARE(默认为 5),再加微调项(如 +1.0) -
io_cost依赖引擎层提供的页面数:InnoDB 用stat_clustered_index_size(聚簇索引叶页数),MyISAM 用Data_length / 1024 / 16 - 系数本身可改,比如
row_evaluate_cost默认 0.1,io_block_read_cost默认 1.0,但改完必须执行FLUSH OPTIMIZER_COSTS才生效
统计信息从哪来?server_cost 和 engine_cost 表才是源头
优化器不是靠硬编码公式算 cost,而是查两张系统表:mysql.server_cost 和 mysql.engine_cost 获取基础系数,再结合表/索引的统计信息动态计算。
-
server_cost控制通用行为:比如key_compare_cost影响索引键比较次数估值,memory_temptable_create_cost影响内存临时表倾向 -
engine_cost按引擎区分:InnoDB 行有io_block_read_cost,MyISAM 行对应另一套值;同一张表换引擎后,cost 估算可能突变 - 常见错误是 UPDATE 了这两张表却没执行
FLUSH OPTIMIZER_COSTS,导致修改完全不生效——MySQL 5.7+ 启动后就把这些值加载进内存了
为什么 EXPLAIN 看不到 cost 数值?得靠 optimizer_trace
EXPLAIN 输出里没有 cost 列,除非你用 EXPLAIN FORMAT=JSON 或开启 optimizer_trace。后者才是观察 cost 决策过程的正路。
- 执行
SET optimizer_trace="enabled=on",再跑你的查询 - 查
SELECT * FROM INFORMATION_SCHEMA.OPTIMIZER_TRACE,看steps数组里每个considered_execution_plans的cost字段 - 重点关注
attaching_conditions_to_tables和analyzing_range_alternatives阶段,那里列出了每个 possible_key 的 cost 对比 - 注意:每个会话只能看到自己语句的 trace,且
OPTIMIZER_TRACE表默认只保留最后一条记录
cost 计算容易踩的坑:统计信息过期、系数失配、回表误估
cost 算不准,往往不是公式问题,而是输入数据错了。
-
ANALYZE TABLE不及时会导致rows和rec_per_key失真,尤其大表批量写入后;SHOW INDEX FROM tbl里的Cardinality偏低,常是主因 - 强行调低
disk_temptable_create_cost(默认 20.0)想“骗”优化器走内存临时表,实际内存不足时仍落盘,反而让copying to tmp table on disk更频繁 - 二级索引非覆盖查询的 cost 估算,会低估回表带来的随机 IO——它按“每行一个 page”粗略计
io_cost,但现实中多行主键可能落在同一页,也可能全散开
真正影响 cost 判断的,从来不是公式本身有多复杂,而是 ANALYZE TABLE 是否及时、FLUSH OPTIMIZER_COSTS 是否漏掉、以及 optimizer_trace 是否打开——这三件事没做对,看再多 cost 数值也没用。


















