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

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 还需要刷新吗?

需要看采集后的数据变化。全量读取描述过去那次采集,不代表直方图自动跟随之后的分布。

重采集后桶数没有增加,是不是失败?

不一定。请求桶数和实际桶数不同,应结合直方图类型、不同值数量及返回诊断解释,最终看它是否能代表关键查询需要的分布。

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