MySQL 直方图适合解决哪类选择性估计问题
来源:17golang原创
时间:2026-11-11 18:53:13 151浏览 收藏
我第一次认真用 MySQL 直方图,是在一个没有合适索引的筛选列上看到优化器估算偏差:SQL 本身不复杂,真正让执行计划犹豫的是它不知道某个常量条件到底能过滤掉多少行。直方图解决的不是“所有慢查询”,而是把列值分布补给优化器,让它在符合条件的常量比较上更接近真实选择性。
官方文档:https://dev.mysql.com/doc/refman/8.4/en/optimizer-statistics.html
如果问题是列值分布太偏、列又没有能提供可靠估算的单列索引,并且查询谓词是对常量的比较,MySQL 直方图才值得优先考虑。它主要改善行数估算,不会自动创建索引,也不会保证执行计划一定变快。
先判断:你遇到的是估算问题吗
直方图适合处理的症状通常不是“磁盘突然变慢”,而是 EXPLAIN 中估算行数与实际返回规模相差很远,导致优化器在过滤顺序、连接方式或访问路径上做出不理想的判断。要先把两个问题分开:
- 如果列上已经有高质量索引,且索引探测能够给出更好的等值估算,直方图未必会被优先使用。
- 如果 SQL 本身读取了大量必须读取的数据,直方图不能替代索引、分区或查询改写。
- 如果数据刚发生大规模变化,旧直方图可能反而把优化器带偏,需要先更新统计。
所以,适用边界可以收窄为:列分布有明显偏斜,查询用常量筛选,优化器缺少足够分布信息,且这个估算会影响后续计划选择。
直方图到底补足了什么信息
普通的列统计往往不能完整表达“哪些值集中出现、哪些值很少见”。直方图把列值范围划成若干桶,并记录累计频率、空值比例、采样比例等信息。优化器可以据此估计一个常量条件大约会保留多少行。
MySQL 8.4 文档列出的可参与直方图估算的谓词包括 =、、>、、>=、、IS NULL、IS NOT NULL、BETWEEN、NOT BETWEEN、IN 和 NOT IN 等与常量比较的形式。函数只要本身是常量表达式,也可能满足这个条件。

可以把它理解成一条信息关系,而不是一条执行流水线:列值分布被压缩成直方图桶,常量谓词从桶中获得选择性线索,优化器再把线索转换为行数估算。图中的箭头表达静态依赖,不表示 SQL 一定按这个顺序执行。
无索引列为什么更容易体现价值
MySQL 官方把直方图描述为主要对非索引列有用。原因很直接:索引本身已经保存了排序或分布相关的信息,优化器在一些等值条件上还能通过 index dive 得到估算;没有索引的列则缺少同等细粒度的线索。
这不等于“只要没索引就应该建直方图”。如果这个列实际上是高频过滤条件,索引也许才是更根本的方案;如果查询很少使用该列,维护直方图的收益可能不足。直方图更像是对计划估算的补充,适合先解决“优化器不知道分布”的局部问题。
| 观察到的情况 | 直方图的优先级 | 判断理由 |
|---|---|---|
| 无索引列,值分布明显偏斜,谓词与常量比较 | 较高 | 直方图能补充缺失的分布信息 |
| 有单列索引,等值条件可通过索引探测估算 | 较低或需对比 | 优化器可能优先使用范围或索引估算 |
| 查询是表达式之间的复杂关联或常量不明确 | 不确定 | 未必落入直方图可参与的谓词范围 |
| 统计长时间未更新,数据分布已经变化 | 先更新再判断 | 过期统计会让估算失真 |
创建直方图时,桶数不是越多越好
使用 ANALYZE TABLE 的直方图语法可以为一列或多列建立统计。桶数的可选范围是 1 到 1024;省略时使用默认值。实际选择不必追求最大值,应该根据分布复杂度、生成成本和估算收益做一个有边界的尝试。
-- 为订单状态和渠道列建立直方图,桶数先取一个可解释的值 ANALYZE TABLE orders UPDATE HISTOGRAM ON status, channel WITH 64 BUCKETS; -- 只更新一个列,不会顺带删除同表其他列的直方图 ANALYZE TABLE orders UPDATE HISTOGRAM ON channel WITH 128 BUCKETS;
如果列的不同值数量不超过桶数,MySQL 可以生成 singleton 类型;如果不同值更多,则可能生成 equi-height 类型,用范围桶表达累计频率。文章中的桶数只是起点,不代表对所有数据集都更优。
查看统计内容,别只看有没有一行记录
直方图通过 INFORMATION_SCHEMA.COLUMN_STATISTICS 查看。真正有用的不只是 HISTOGRAM 是否存在,还包括最后更新时间、采样比例、桶数和直方图类型。
-- 只查看目标表和列,避免把别的统计混进判断
SELECT
SCHEMA_NAME,
TABLE_NAME,
COLUMN_NAME,
HISTOGRAM->>'$."histogram-type"' AS histogram_type,
HISTOGRAM->>'$."sampling-rate"' AS sampling_rate,
HISTOGRAM->>'$."last-updated"' AS last_updated,
JSON_LENGTH(HISTOGRAM->>'$."buckets"') AS bucket_count
FROM INFORMATION_SCHEMA.COLUMN_STATISTICS
WHERE TABLE_NAME = 'orders'
AND COLUMN_NAME IN ('status', 'channel');
sampling-rate 小于 1 表示生成统计时只读取了部分数据。大表上这可能是合理的资源取舍,但它也提醒你:不要把一份采样得到的直方图当成永久精确事实。
用 EXPLAIN 看估算是否回到了合理范围
建立直方图后,应该回到原来的查询观察估算列,而不是只看到“创建成功”就结束。MySQL 文档指出,直方图发挥作用时,其影响可以在 EXPLAIN 的 filtered 等估算信息中体现。
-- 用同一条业务查询观察过滤条件的估算变化 EXPLAIN SELECT order_id, customer_id FROM orders WHERE channel = 'partner' AND status = 'pending';
这里关注的是估算是否更贴近数据特征,以及后续计划是否因此更合理,而不是盲目追求某个固定百分比。若查询列已有索引,优化器可能继续使用范围估算或索引探测;这属于正常的优先级关系,不表示直方图创建失败。
哪些情况说明直方图帮不上忙
我会把以下几种情况视为停止扩展直方图的信号:
- 实际瓶颈是回表、排序、锁等待或磁盘 I/O,估算只是表面现象。
- 谓词没有稳定的常量比较,无法清楚地落到文档列出的适用形式。
- 列已有可靠索引估算,新增直方图并没有改变计划判断。
- 数据变化频繁,而直方图长期不更新,统计新鲜度本身成为新问题。
- 列是
JSON或空间类型,不能直接按普通列直方图生成。
尤其要注意,直方图是在数据变更后按需创建或更新的统计,不会像索引那样随每次修改自动维护。它的维护成本低于索引变更,但过期也会逐渐积累。
不合适时如何撤销
如果对比后确认某列的直方图不再有用,可以只删除该列的统计,保留同表其他列。
-- 只移除 channel 的直方图,保留 status 等其他列的统计 ANALYZE TABLE orders DROP HISTOGRAM ON channel;
撤销的意义是把统计选择恢复到更简单的状态,方便重新观察索引和基础统计的行为。它不是回滚数据,也不会删除表上的索引。

最后用一张表做决定
| 问题 | 回答“是”时的动作 |
|---|---|
| 估算行数和实际规模差距明显吗? | 继续确认谓词和列分布,不要直接把慢归因于直方图 |
| 列值分布明显偏斜吗? | 优先考虑建立直方图补充分布信息 |
| 查询是对常量的比较吗? | 检查是否属于等值、范围、IN、NULL 等支持形式 |
| 列没有可靠的索引估算吗? | 把直方图作为局部估算补充,而不是索引替代品 |
| 统计还新鲜吗? | 数据变化后重新生成,再用 EXPLAIN 对比 |
我的结论是:MySQL 直方图最适合解决“优化器不了解某列值分布”造成的选择性估计问题,尤其是无索引列上的常量过滤。它的价值在于让计划选择拥有更好的输入,而不是承诺一次设置就能解决所有查询性能问题。
相关问题
直方图会自动创建索引吗?
不会。直方图只是优化器统计,索引仍然需要单独设计和维护。
桶数应该直接设置成 1024 吗?
不建议把最大值当成默认答案。先根据分布复杂度和资源成本选择可解释的桶数,再用估算变化判断是否值得增加。
建立直方图后执行计划一定改变吗?
不一定。若原有范围估算或索引探测更可靠,优化器可以继续采用它们;直方图存在并不意味着必须使用。
-
374 收藏
-
499 收藏
-
384 收藏
-
184 收藏
-
265 收藏
-
356 收藏
-
459 收藏
-
312 收藏
-
428 收藏
-
345 收藏
-
133 收藏
-
353 收藏
-
201 收藏
-
209 收藏
-
399 收藏
-
278 收藏
-
389 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 485次学习