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行,而且慢的点正好出现在过滤或者排序节点,说明优化器手里的分布统计信息可能已经过时,或者精度太糙。

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

哪些场景不能把直方图当万能修复方案
第一,查询的主要过滤条件已经有选择性很好的联合索引,问题其实出在索引列顺序或者范围条件的设计不合理。第二,表内数据每天变化幅度很大,昨天生成的分布统计今天已经完全失真。第三,过滤条件写在函数里、有隐式类型转换或者套在复杂表达式里,统计信息没法覆盖真实的选择性。第四,真正的问题是查询返回了太多列,或者排序操作没法利用索引,只改行数预估根本不会减少I/O开销。
碰到这些情况,优先检查 SHOW INDEX FROM orders、表数据增长情况、排序节点开销和慢查询的真实样本。直方图是有明确依据的补充优化手段,不能替代正常的索引设计流程。
没效果的时候怎么恢复原有状态
如果复测之后看不到收益,或者上线之后数据分布已经发生偏移,可以直接删掉这一列的直方图:
ANALYZE TABLE orders
DROP HISTOGRAM ON status;
撤销之后再跑一遍同一条 EXPLAIN ANALYZE,确认执行计划回到之前的预期状态。生产环境的变更还要记录执行时间、影响的表、复测SQL和回退结果;ANALYZE TABLE 默认会写入二进制日志,主从复制环境要结合业务低峰窗口评估操作影响。
相关问题
直方图会自动创建索引吗?
不会。它只提供列值分布的统计信息,索引还是要靠表结构设计和匹配查询条件来手动创建。
没有索引的列也能使用直方图吗?
可以。直方图的核心价值之一就是补充普通索引统计没法表达的列分布细节,但优化器最终会不会选用它的结果,还是要看执行计划和实际耗时表现。
生成直方图之后执行计划怎么没变化?
有可能是原执行计划本身就足够高效,也有可能查询的瓶颈根本不在行数选择性估算,或者统计信息没覆盖到真正影响计划走向的条件列。
应该多久重建一次直方图?
不要按固定天数盲目重建。以数据分布明显变化、执行计划异常回退或者慢查询指标恶化为触发条件,而且每次重建都要在目标查询上做前后效果复测。
把验证结果整理成可复用的小清单
做直方图实验最有价值的产物不是一条操作SQL,而是一组可回溯复查的证据:生成前后的 EXPLAIN ANALYZE、COLUMN_STATISTICS 记录、总耗时、数据量变化和回退验证结果。只有行数预估误差、访问路径和实际耗时都朝着预期方向改善,才值得把这个直方图留在生产环境。
-
374 收藏
-
499 收藏
-
384 收藏
-
184 收藏
-
265 收藏
-
300 收藏
-
109 收藏
-
421 收藏
-
419 收藏
-
238 收藏
-
数据库 · MySQL | 1天前 | MySQL · 回滚 · 数据库运维 · 配置变更 · 系统变量 · MySQL 8.4 SET PERSIST SET PERSIST_ONLY RESET PERSIST mysqld-auto.cnf persisted_variables296 收藏
-
数据库 · MySQL | 1天前 | MySQL · 回滚 · 数据库运维 · 配置变更 · 系统变量 · MySQL 8.4 SET PERSIST SET PERSIST_ONLY RESET PERSIST mysqld-auto.cnf persisted_variables244 收藏
-
数据库 · MySQL | 1天前 | MySQL · sql优化 · 数据库运维 · 性能排查 · 优化器提示 · MySQL 8.4 SET_VAR optimizer hint sort_buffer_size SQL 性能隔离497 收藏
-
数据库 · MySQL | 2天前 | MySQL · DDL · 元数据锁 · 性能排查 · performance_schema · MySQL 元数据锁 metadata_locks performance_schema DDL阻塞 Waiting for table metadata lock297 收藏
-
数据库 · MySQL | 2天前 | MySQL · 索引 · 执行计划 · sql优化 · 性能排查 · 执行计划 MySQL 8.4 EXPLAIN FORMAT=JSON cost_info query_cost239 收藏
-
数据库 · MySQL | 2天前 | MySQL · 索引 · 执行计划 · sql优化 · 性能排查 · 执行计划 MySQL 8.4 EXPLAIN FORMAT=JSON cost_info query_cost284 收藏
-
数据库 · MySQL | 2天前 | MySQL · 复制 · 主键 · InnoDB · 数据库迁移 · 数据库迁移 MySQL 8.4 sql_generate_invisible_primary_key 生成不可见主键 my_row_id419 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 485次学习