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

MySQL 直方图统计信息怎样帮助优化器选择计划

来源:17golang原创

时间:2026-10-28 23:15:37 400浏览 收藏

MySQL 直方图不是索引,也不会强制优化器选择某个索引;它补充的是列值分布信息。对于值分布倾斜、但仅靠普通统计信息难以估准选择率的列,优化器可以利用直方图更接近地估算过滤后会剩下多少行,从而比较不同执行计划。

官方资料:https://dev.mysql.com/doc/refman/8.4/en/optimizer-statistics.html

要点速览
  • 用 ANALYZE TABLE ... UPDATE HISTOGRAM 为具体列生成统计信息,默认 100 个桶,也可以显式指定 1 到 1024 个桶。
  • 用 INFORMATION_SCHEMA.COLUMN_STATISTICS 查看直方图 JSON、类型、桶数和 sampling-rate,再用 EXPLAIN 对照优化器估算。
  • 直方图依赖数据分布的新鲜度;效果不合适时先重新生成,仍不需要时用 DROP HISTOGRAM 清除。

直方图到底补充了哪类统计信息

优化器需要估算条件会命中多少行,才能在索引、扫描、连接顺序等候选计划之间做比较。单一的基数或平均分布假设,在列值高度倾斜时可能不够:例如某个状态值占了绝大多数行,另一些状态几乎没有数据,同样的列却对应完全不同的过滤选择率。

直方图把列值范围或离散值整理成多个桶,并把这些分布摘要保存在数据字典中。用户通过 INFORMATION_SCHEMA.COLUMN_STATISTICS 读取视图,不直接操作底层数据字典表。它提供的是优化器参考信息,不是查询结果缓存,也不会替代合适的索引。

MySQL 过滤列、列值分布、直方图桶、COLUMN_STATISTICS、选择率估算与 EXPLAIN 计划的静态关系说明图
图1:结构说明图,展示列值分布、直方图统计与优化器计划判断的静态关系,不是运行截图。

用 ANALYZE TABLE 为目标列生成直方图

先挑选真正参与过滤、分布明显不均匀或估算经常偏差的列,而不是给整张表的所有列都建立直方图。最小语句如下:

-- 为 orders.status 生成直方图,帮助优化器了解状态值分布。
ANALYZE TABLE orders
  UPDATE HISTOGRAM ON status;

-- 需要更细的分布摘要时显式指定桶数量。
ANALYZE TABLE orders
  UPDATE HISTOGRAM ON status WITH 64 BUCKETS;

UPDATE HISTOGRAM 语法一次只针对一张表;同一语句可以列出多个目标列。WITH N BUCKETS 的 N 范围是 1 到 1024,省略时默认 100。桶越多不等于计划一定越好,还要考虑生成成本、数据规模和分布复杂度。

该操作需要目标表的 SELECT 和 INSERT 权限。MySQL 8.4 文档还说明,几何类型和 JSON 列不支持直方图,单列唯一索引覆盖的列也不能生成直方图;临时表和加密表同样不在可用范围内。

先查看 COLUMN_STATISTICS,再判断估算是否有依据

生成后不要只看语句返回成功,还要知道统计信息是否确实存在、采样比例是多少:

-- 读取指定表和列的直方图摘要,避免把别的表混在一起看。
SELECT SCHEMA_NAME,
       TABLE_NAME,
       COLUMN_NAME,
       HISTOGRAM
FROM INFORMATION_SCHEMA.COLUMN_STATISTICS
WHERE SCHEMA_NAME = DATABASE()
  AND TABLE_NAME = 'orders'
  AND COLUMN_NAME = 'status';

-- 只取采样比例,1.0 表示生成时读取了全部数据。
SELECT HISTOGRAM->>'$."sampling-rate"' AS sampling_rate
FROM INFORMATION_SCHEMA.COLUMN_STATISTICS
WHERE SCHEMA_NAME = DATABASE()
  AND TABLE_NAME = 'orders'
  AND COLUMN_NAME = 'status';

返回的 HISTOGRAM 是 JSON 对象,常见信息包括 histogram-type、number-of-buckets-specified、last-updated、null-values 和 sampling-rate。sampling-rate 小于 1 说明生成过程采用了采样,不能把桶看成完整数据的逐行复刻。

如果生成期间需要读取的数据超过 histogram_generation_max_mem_size,MySQL 会改为采样;可以通过这个字段判断当前直方图的依据是否足够覆盖业务数据。这里的判断重点是“统计信息是否新鲜、是否有采样、是否与当前过滤场景相符”,而不是单独追求一个更大的桶数。

MySQL ANALYZE TABLE、UPDATE HISTOGRAM、WITH N BUCKETS、AUTO UPDATE、sampling-rate 与 DROP HISTOGRAM 的静态维护边界图
图2:维护边界说明图,展示桶数、采样率和生命周期控制的静态关系,不是运行截图。

用 EXPLAIN 对照计划,别把直方图当成强制提示

直方图是否真正改变了优化器选择,要回到具体查询观察估算行数、访问方式和过滤条件。可以在生成前后分别保存同一条查询的计划:

-- 用同一条查询对照估算行数和访问方式,避免把查询文本也一起改掉。
EXPLAIN FORMAT=JSON
SELECT order_id, created_at
FROM orders
WHERE status = 'pending';

重点对照的是估算行数、过滤条件对应的选择率以及访问路径是否更符合数据分布。计划变化本身不是成功标准:如果直方图过期、采样不足,或者优化器基于新估算选择了更差的路径,查询仍可能变慢。应在接近真实数据量和真实条件的环境中,用同一条 SQL、同一组参数做前后对照。

直方图也不能表达任意列之间的相关性。两个条件分别看似稀疏,组合起来可能高度相关;单列直方图并不会自动得到完整的多列联合分布。因此它适合补充某个列的分布估计,不应被当成“有了统计信息就能解决所有计划问题”。

桶数量、采样与自动更新怎么取舍

小桶数适合分布简单、维护成本敏感的列;分布有多个明显密集区时,可以增加桶数,但要用 HISTOGRAM 的桶数和 sampling-rate 一起判断。只调大桶数而不关注数据是否采样,可能只是让摘要更复杂,并没有带来等比例的估算收益。

-- 让后续持久化统计更新时按同样桶数自动更新该列直方图。
ANALYZE TABLE orders
  UPDATE HISTOGRAM ON status
  WITH 64 BUCKETS AUTO UPDATE;

-- 如果自动更新带来的计划变化不符合预期,删除这一列的直方图。
ANALYZE TABLE orders
  DROP HISTOGRAM ON status;

AUTO UPDATE 会启用自动更新;不写时默认是 MANUAL UPDATE。自动更新依赖持久化统计信息的更新机制,并不意味着每次 DML 完成后直方图都会立刻同步。若数据刚完成大批量导入、分布发生明显变化且需要立即刷新,显式执行 ANALYZE TABLE 更直接。

常见问题

直方图会不会强制 MySQL 使用某个索引?

不会。它提供列值分布摘要,影响的是优化器的估算依据;最终计划仍由优化器结合成本、索引和其他条件决定。

桶数是不是越大越好?

不是。桶数要与分布复杂度、表规模、生成成本和采样情况一起看。先用默认值观察估算,再针对确有偏差的列调整。

什么时候应该删除直方图?

当数据分布已变化、统计信息长期过期,或对照同一查询发现新计划持续变差时,可以先重新生成;如果该列不再需要分布摘要,再执行 DROP HISTOGRAM 清除。

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