MySQL 8.0Notes

第 11 章:代价模型

zjc 于 2026-01-11 发布

这是《MySQL 8.0 源码与内核实战》的独立章节版。本章从概念、实操和生产排查三个视角展开,代码块保留了原书可直接运行的版本。 优化器用代价模型把 IO、CPU、内存和网络估算统一为可比较的成本。代价不是精确时间,而是相对排序依据。

11.1 代价来源

成本 示例
IO 页读取、回表、临时表
CPU 表达式计算、比较、聚合
memory join buffer、sort buffer
engine estimate 引擎返回的扫描代价
first row LIMIT 场景

估算输入:

  1. 表行数;
  2. 索引基数;
  3. range 区间数;
  4. 记录长度;
  5. 页大小;
  6. 缓存命中率假设;
  7. 条件选择率;
  8. 连接顺序。

11.2 系统表

代价常量存储在引擎代价表中:

SELECT * FROM mysql.engine_cost;
SELECT * FROM mysql.server_cost;

常见项:

成本
engine_cost io block read
server_cost row evaluate
server_cost memory tmp table create
server_cost disk tmp table create

修改示例:

UPDATE mysql.engine_cost
SET cost_value=2.0
WHERE cost_name='io_block_read_cost';
FLUSH OPTIMIZER_COSTS;

生产环境修改必须有基线、回滚和评测,不应作为常规调优手段。

11.3 选择率

等值条件常见思路:

selectivity = 1 / distinct_values

范围条件依赖:

  1. 列类型;
  2. min/max;
  3. 直方图;
  4. 索引统计;
  5. 条件形式;
  6. NULL 比例;
  7. 字符集。

查看直方图:

ANALYZE TABLE orders UPDATE HISTOGRAM ON status, user_id;
SELECT * FROM information_schema.column_statistics\G

直方图适合列相关性弱但数据倾斜明显的场景。

11.4 单表代价

scan cost
  = estimated pages * io cost
  + estimated rows * row evaluate cost

index lookup cost
  = range count * io cost
  + matching rows * row evaluate cost
  + back to clustered index cost

影响判断:

  1. 预估返回行数;
  2. 是否覆盖索引;
  3. 回表页分散程度;
  4. LIMIT;
  5. ORDER BY;
  6. 统计准确性。

11.5 连接代价

Nested Loop:

cost = outer cost
     + outer rows * inner lookup cost

Hash Join:

cost = build input scan + hash build
     + probe input scan + hash probe

优化器会依据估算行数、可用索引和内存代价选择。无索引等值大表连接常更容易走 hash join。

11.6 覆盖索引

SELECT user_id, COUNT(*)
FROM orders
WHERE status=1
GROUP BY user_id;

若存在:

KEY idx_status_user(status,user_id)

二级索引可能覆盖查询,避免聚簇索引回表。覆盖索引收益取决于:

  1. 返回列;
  2. 索引列顺序;
  3. 行宽;
  4. 范围条件;
  5. 排序分组需求。

11.7 LIMIT 与 first row

SELECT * FROM orders ORDER BY created_at DESC LIMIT 10;

如果有 idx_created(created_at),按索引反向读取前 10 行可能代价很低。无索引时则必须扫描并排序更多行。

优化器会区分:

  1. 全量执行代价;
  2. 找到第一批行的代价;
  3. 是否可提前停止。

11.8 代价模型局限

  1. 统计是估算;
  2. 缓存效果难以精确建模;
  3. 相关列分布难处理;
  4. 远程存储延迟差异大;
  5. 运行时并发影响 IO;
  6. UDF 和复杂函数代价难估计;
  7. 参数化值不同导致计划差异。

因此执行计划可能随数据分布变化。

11.9 调优流程

慢 SQL
  -> EXPLAIN ANALYZE
     -> compare estimated and actual rows
        -> check statistics / histogram
           -> analyze access path
              -> rewrite SQL or index
                 -> regression test

常见修复:

  1. 补充合适索引;
  2. 收集统计;
  3. 增加直方图;
  4. 改写条件;
  5. 消除隐式转换;
  6. 减少返回列;
  7. 拆分复杂 SQL;
  8. 局部使用 hint。

本章小结

代价模型把页 IO、行计算、临时表和连接成本折算为相对值,用于选择访问路径和连接顺序。理解统计、选择率、覆盖索引、first row 和模型局限,才能正确解读执行计划。

思考题

  1. 代价单位和执行时间是否等价?
  2. 为什么等值条件常用基数倒数估算选择率?
  3. 直方图适合解决什么统计问题?
  4. 覆盖索引如何改变代价?
  5. EXPLAIN ANALYZE 中估算行数和实际行数差异说明什么?