登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  数据库 >  MySQL

MySQL 8.0 直方图统计何时能救回错误执行计划:从基线到验证

来源:17golang原创

时间:2026-07-27 12:05:04 420浏览 收藏

线上订单列表查询耗时突然从 180ms 涨到 1.6s,慢查询里最反常的点不是索引失效,而是 MySQL 预估只会命中几百行,实际却扫出了几十万行。碰到这种“看起来有合适索引,但执行计划完全不符合真实数据分布”的场景,MySQL 8.0 的列直方图可以先做小范围尝试验证。

要点速览

  • 直方图补充的是非索引列或者数据分布的估算信息,不会凭空创建索引。
  • 先用 EXPLAIN ANALYZE 记录预估行数和实际行数,再决定要不要生成直方图。
  • 生成之后要检查 INFORMATION_SCHEMA.COLUMN_STATISTICS,再用同一条查询做复测。
  • 数据分布变化、采样误差或者查询条件不匹配时,直方图可能起不到作用,必要时直接删掉即可。

先确认问题是“行数估错”,不是“缺索引”

示例场景是订单列表查询,只关心最近 30 天里处于指定状态的订单:

SELECT id, user_id, amount, created_at
FROM orders
WHERE status = '待人工复核'
  AND created_at >= NOW() - INTERVAL 30 DAY
ORDER BY created_at DESC
LIMIT 50;

假设 status 的值高度倾斜:绝大多数订单是“已完成”状态,只有很小一部分需要人工复核。先别急着加复合索引,先把当前的执行计划和真实扫行数记下来:

EXPLAIN ANALYZE
SELECT id, user_id, amount, created_at
FROM orders
WHERE status = '待人工复核'
  AND created_at >= NOW() - INTERVAL 30 DAY
ORDER BY created_at DESC
LIMIT 50;

重点看每个执行节点对应的 rows=actual ... rows=。如果预估只有400行、实际返回280000行,而且慢的点正好出现在过滤或者排序节点,说明优化器手里的分布统计信息可能已经过时,或者精度太糙。

MySQL 订单状态倾斜导致估算行数与实际行数分叉的基线证据图

用最小成本实验生成 orders.status 列的直方图

直方图的作用是帮优化器搞清楚列值的真实分布,尤其适合条件列没有合适索引,或者普通索引的基数统计没法表达倾斜分布的场景。操作前先在可回滚的测试环境,或者业务低峰期做验证:

ANALYZE TABLE orders
  UPDATE HISTOGRAM ON status WITH 32 BUCKETS;

这条语句会把 status 的分布统计写入数据字典。桶数不是越大越好,第一轮验证用32个桶基本足够,桶数太大不仅会增加统计维护成本,还可能让小表的统计结果过度精细,反而干扰判断。

生成操作跑完之后,先确认数据库是不是真的存下了对应的统计信息:

SELECT SCHEMA_NAME, TABLE_NAME, COLUMN_NAME, HISTOGRAM
FROM INFORMATION_SCHEMA.COLUMN_STATISTICS
WHERE TABLE_SCHEMA = DATABASE()
  AND TABLE_NAME = 'orders'
  AND COLUMN_NAME = 'status';

如果查不到对应的记录,别以为语句执行成功查询就一定会变快。大概率是权限不足、列名写错、统计信息缓存没刷新,也有可能这条查询本身就不适合用直方图优化。

用同一组参数对比执行计划的变化

复测的时候要保证SQL、数据快照和运行参数完全一致,至少记录三个核心数值:总查询耗时、预估行数、实际扫描行数。示例记录格式可以参考:

-- 记录复测时间,不要改写 WHERE 条件
EXPLAIN ANALYZE
SELECT id, user_id, amount, created_at
FROM orders
WHERE status = '待人工复核'
  AND created_at >= NOW() - INTERVAL 30 DAY
ORDER BY created_at DESC
LIMIT 50;

如果直方图生效,通常能看到过滤节点的预估行数会更贴近真实值,随之生成更合理的访问路径。别死盯着“用了哪个索引”判断效果,如果结果集本身就很大,执行计划变了但总耗时没降,说明瓶颈大概率在回表、排序、磁盘读或者返回的数据量本身。

MySQL 直方图写入 COLUMN_STATISTICS 后通过 EXPLAIN ANALYZE 对比前后计划的验证图

哪些场景不能把直方图当万能修复方案

第一,查询的主要过滤条件已经有选择性很好的联合索引,问题其实出在索引列顺序或者范围条件的设计不合理。第二,表内数据每天变化幅度很大,昨天生成的分布统计今天已经完全失真。第三,过滤条件写在函数里、有隐式类型转换或者套在复杂表达式里,统计信息没法覆盖真实的选择性。第四,真正的问题是查询返回了太多列,或者排序操作没法利用索引,只改行数预估根本不会减少I/O开销。

碰到这些情况,优先检查 SHOW INDEX FROM orders、表数据增长情况、排序节点开销和慢查询的真实样本。直方图是有明确依据的补充优化手段,不能替代正常的索引设计流程。

没效果的时候怎么恢复原有状态

如果复测之后看不到收益,或者上线之后数据分布已经发生偏移,可以直接删掉这一列的直方图:

ANALYZE TABLE orders
  DROP HISTOGRAM ON status;

撤销之后再跑一遍同一条 EXPLAIN ANALYZE,确认执行计划回到之前的预期状态。生产环境的变更还要记录执行时间、影响的表、复测SQL和回退结果;ANALYZE TABLE 默认会写入二进制日志,主从复制环境要结合业务低峰窗口评估操作影响。

相关问题

直方图会自动创建索引吗?

不会。它只提供列值分布的统计信息,索引还是要靠表结构设计和匹配查询条件来手动创建。

没有索引的列也能使用直方图吗?

可以。直方图的核心价值之一就是补充普通索引统计没法表达的列分布细节,但优化器最终会不会选用它的结果,还是要看执行计划和实际耗时表现。

生成直方图之后执行计划怎么没变化?

有可能是原执行计划本身就足够高效,也有可能查询的瓶颈根本不在行数选择性估算,或者统计信息没覆盖到真正影响计划走向的条件列。

应该多久重建一次直方图?

不要按固定天数盲目重建。以数据分布明显变化、执行计划异常回退或者慢查询指标恶化为触发条件,而且每次重建都要在目标查询上做前后效果复测。

把验证结果整理成可复用的小清单

做直方图实验最有价值的产物不是一条操作SQL,而是一组可回溯复查的证据:生成前后的 EXPLAIN ANALYZECOLUMN_STATISTICS 记录、总耗时、数据量变化和回退验证结果。只有行数预估误差、访问路径和实际耗时都朝着预期方向改善,才值得把这个直方图留在生产环境。

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>