MySQL 直方图怎样判断统计信息是否需要重新采集
来源:17golang原创
时间:2026-11-04 04:39:35 493浏览 收藏
判断 MySQL 直方图是否需要重新采集,要把目标列的数据分布变化、直方图元数据和查询过滤估算放在一起看。更新时间很早只是线索;如果关键取值的占比已经变化,而且相关谓词的估算持续偏离实际,重采集才更有依据。MySQL 没有为所有业务规定一个通用的“过期天数”或“变化百分比”。
本文以 MySQL 8.4 为例,使用一个原创订单场景说明判断方法。语句供读者按实际表结构使用,本文没有连接数据库运行,也不提供虚构执行计划或性能提升数字。
官方文档:https://dev.mysql.com/doc/refman/8.4/en/optimizer-statistics.html
从估算偏差找到需要关注的列
假设订单表中的 fulfillment_state 没有索引,近期批量迁入了很多 waiting 订单,相关查询开始变慢。先保留原 SQL、实际参数、执行计划和相近负载下的耗时,再问:优化器是不是低估或高估了这个状态的过滤效果?如果估算本来就接近实际,慢的原因可能是扫描量、锁等待或存储访问,直方图不一定是主要矛盾。
直方图帮助优化器估计列值的选择性,不是查询索引,也不会使没有访问路径的条件自动拥有索引。一个查询同时涉及多个条件时,应找具体过滤节点,别把最终返回行数和任意中间节点的估计直接比较。连接内层节点存在多次循环时,还要明确实际行数的每循环口径。
读取直方图元数据与更新模式
-- 在自己的MySQL 8.4实例查看实际版本与目标列的直方图元数据。
SELECT VERSION();
SELECT SCHEMA_NAME, TABLE_NAME, COLUMN_NAME,
HISTOGRAM->>'$."last-updated"' AS updated_utc,
HISTOGRAM->>'$."sampling-rate"' AS sample_rate,
HISTOGRAM->>'$."histogram-type"' AS histogram_type,
HISTOGRAM->>'$."auto-update"' AS auto_update,
HISTOGRAM->>'$."number-of-buckets-specified"' AS requested_buckets,
JSON_LENGTH(HISTOGRAM->'$.buckets') AS actual_buckets
FROM information_schema.COLUMN_STATISTICS
WHERE SCHEMA_NAME = 'shop'
AND TABLE_NAME = 'orders'
AND COLUMN_NAME = 'fulfillment_state';
COLUMN_STATISTICS 按库、表、列保存 HISTOGRAM。查询为空时,先确认对象名和当前账号可见权限,再判断目标列是否没有直方图;没有采集过与已有统计陈旧是两种情况。last-updated 是生成时刻,使用 UTC,和本地导入日志比较时要先统一时区。sampling-rate 描述上次采集覆盖比例,不代表当前有效率;即使等于 1,也只能说明生成时读取了全部数据,不能说明后来没有变化。
requested_buckets 是请求桶数,actual_buckets 是实际桶数。取值较少时可形成 singleton,桶数少于请求值并不等于采集失败。equi-height 的桶代表区间;桶里的频率是累计频率,不能直接把每个桶的累计值当成该区间的独立占比。

8.4 支持 MANUAL UPDATE 与 AUTO UPDATE,默认是手动更新。自动模式会在表执行 ANALYZE TABLE 以及 InnoDB 后台重算持久统计时更新相应直方图,它不是每次行修改都同步重建。旧版本或兼容数据库不能直接照搬该选项,应以自己的版本文档为准。
元数据说明:https://dev.mysql.com/doc/refman/8.4/en/information-schema-column-statistics-table.html
比较业务数据分布而非只看时间
-- 示例假定orders已有fulfillment_state列;统计会读取数据,先评估成本。 SELECT fulfillment_state, COUNT(*) AS row_count FROM shop.orders GROUP BY fulfillment_state; -- 先观察固定字面量的估算;此语句不执行目标SELECT。 EXPLAIN FORMAT=JSON SELECT id FROM shop.orders WHERE fulfillment_state = 'waiting'; -- ANALYZE会执行查询,只在可承受的测试副本或维护窗口运行。 EXPLAIN ANALYZE SELECT id FROM shop.orders WHERE fulfillment_state = 'waiting';
优先使用已有导入记录、可靠的业务汇总,了解新增或删除的数据主要落在哪些取值。整表 GROUP BY 是直观示例,但可能读取很多行,不应给高峰生产库临时加上昂贵的统计工作。用有限样本替代时必须说明样本来源;按主键只取最前一段,可能恰好遗漏最近导入的数据。
最值得关注的变化通常不是表多了几行,而是重要取值占比、NULL 比例、新出现的集中取值或范围边界改变。若所有取值近似同比增加,原有选择性未必明显失真;若表总行数几乎不变,却把某类订单批量改成另一状态,直方图反而可能很快失去代表性。判断阈值应服务具体查询和资源预算,不能从一个固定百分比推导所有表都要重采集。
EXPLAIN FORMAT=JSON 可先看估算;EXPLAIN ANALYZE 会真实执行查询,需在可承受的环境中进行。比较同一过滤节点的 estimated rows 与 actual rows,并覆盖常见取值和稀有取值。若业务数据正在大量变化,尽量使用一致的数据快照或稳定窗口,避免把两个不同时间的数据当成模型误差。
只刷新有证据的列
-- 示例桶数并非通用最优值,先记录旧直方图及请求桶数。 SELECT HISTOGRAM FROM information_schema.COLUMN_STATISTICS WHERE SCHEMA_NAME = 'shop' AND TABLE_NAME = 'orders' AND COLUMN_NAME = 'fulfillment_state'; -- 只重采集已确认失真的目标列;MANUAL UPDATE明确使用手动模式。 ANALYZE TABLE shop.orders UPDATE HISTOGRAM ON fulfillment_state WITH 64 BUCKETS MANUAL UPDATE; -- 如维护策略决定启用8.4自动更新,用此语句替代上面的手动模式语句。 -- 不要在一次排查中来回切换,两种模式应择一执行。 -- ANALYZE TABLE shop.orders UPDATE HISTOGRAM ON fulfillment_state -- WITH 64 BUCKETS AUTO UPDATE;
UPDATE HISTOGRAM 只作用于指定列,重采集前保存旧 JSON、桶数、模式和相关查询计划,方便理解变化。64 只是示例;对于取值很少的状态列,盲目增加桶数未必增加有效信息。对于大量连续取值,桶数、采样代表性和数据倾斜需要分别分析,不能只凭桶数判断质量。
采集会消耗读取和内存资源,也涉及表维护权限及锁的影响,应放到评估过的窗口执行。检查 ANALYZE 返回的目标列诊断信息,再读取元数据确认预定列已被更新。普通 ANALYZE TABLE 的键分布统计与显式直方图管理不能混为一谈;8.4 自动模式会参与普通 ANALYZE 的更新,但手动模式不应仅靠普通 ANALYZE 假定已经刷新。

维护语法:https://dev.mysql.com/doc/refman/8.4/en/analyze-table.html
比较同一查询并识别重采集无效的情况
重采集后,以同一 SQL、同一组参数和可比较的数据重新观察估算与耗时。时间戳变新说明统计重新生成,并不等于业务查询一定更快。若估算更接近实际但计划不变,可能现有访问路径仍然最合算;若计划变了却更慢,要检查其他查询、参数和缓存状态,避免只看单次耗时就宣布成功。
直方图主要用于合适的列与常量比较,范围优化器的行数估算可优先于直方图。有索引不代表直方图绝对无用,但如果当前估算来自索引访问,重复重建列直方图可能不会改变它。两个列强相关导致的组合选择性错误,也不是刷新两个单列分布就一定能解决。
| 观察到的线索 | 建议判断 |
|---|---|
| 时间较早,分布与估算稳定 | 先保留现状,按业务变化观察 |
| 重要取值占比改变,过滤估算偏离 | 优先评估目标列重采集 |
| 时间新,但样本遗漏重要取值 | 分析采样代表性,单纯再采一次不保证解决 |
| 估算准确但查询仍慢 | 检查访问路径、执行成本和等待 |
| 多列相关性导致组合偏差 | 检查查询与索引设计,避免无限重采集 |
多久重采集一次合适?
根据分布变化速度、关键查询估算和维护成本制定周期,大批导入或集中状态迁移后增加一次针对性观察,不把“每天”当成统一标准。
采样率为 1 还需要刷新吗?
需要看采集后的数据变化。全量读取描述过去那次采集,不代表直方图自动跟随之后的分布。
重采集后桶数没有增加,是不是失败?
不一定。请求桶数和实际桶数不同,应结合直方图类型、不同值数量及返回诊断解释,最终看它是否能代表关键查询需要的分布。
-
501 收藏
-
501 收藏
-
501 收藏
-
501 收藏
-
501 收藏
-
152 收藏
-
498 收藏
-
309 收藏
-
325 收藏
-
117 收藏
-
207 收藏
-
331 收藏
-
101 收藏
-
160 收藏
-
339 收藏
-
107 收藏
-
108 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 485次学习