直方图统计是什么
直方图(histogram)是 MySQL 8.0 引入的列级统计对象:把一列的取值分布切成若干桶记录下来,优化器读它来估算某个过滤条件能命中多少行(选择性),再据此选访问路径与连接顺序。它不改变数据存储,也不提供快速定位能力,只影响「估算」这一环,所以不会像索引那样把扫描量降下来。
基本信息
| 适用版本 | MySQL 8.0 及以上 |
|---|---|
| 作用对象 | 单个列;不支持对表达式或多列组合生成 |
| 桶数范围 | 1~1024,不写默认 100 |
| 查看位置 | information_schema.COLUMN_STATISTICS |
| 是否持久化 | 写入数据字典,实例重启后仍在 |
| 更新方式 | 数据变化后需重新执行生成语句,不会自动刷新 |
典型使用场景
- 无索引列过滤:WHERE 里高频出现却没有索引的列,靠它把行数估算拉回合理区间
- 分布严重倾斜:少数取值占了绝大多数行,优化器默认的均匀假设会算错
- 范围条件走不了索引:扫描量压不下来,但估算准了仍能选到更合适的计划
- 连接顺序排查:连接列统计不准导致选错驱动表时,先补统计再看计划
- 多列组合查询:单列分布修正后,组合条件的选择性估算误差随之下降
生成与查看
生成与查看
ANALYZE TABLE orders UPDATE HISTOGRAM ON status, city WITH 64 BUCKETS; # 为两列各生成 64 桶直方图
SELECT JSON_EXTRACT(histogram, '$.histogram-type') AS htype, # 桶类型:singleton 或 equi-height
JSON_EXTRACT(histogram, '$.last-updated') AS updated # 该列直方图的生成时间
FROM information_schema.COLUMN_STATISTICS
WHERE table_name = 'orders' AND column_name = 'status'; # 按表名与列名过滤
ANALYZE TABLE orders DROP HISTOGRAM ON status; # 不再需要时删除
EXPLAIN SELECT * FROM orders WHERE status = 'paid'; # 对比 rows 估算与真实行数统计内容逐项读法
| 字段 | 含义 | 怎么看 |
|---|---|---|
| SCHEMA_NAME/TABLE_NAME/COLUMN_NAME | 这条统计属于哪个库、哪张表的哪一列 | 一列一条记录,多库同名表靠库名区分 |
| histogram-type | 桶的组织方式:singleton 每个取值一桶,equi-height 按累计频率等高分桶 | 取值种类少时自动用 singleton |
| buckets | 桶列表:记录边界值、累计频率与不同值个数 | 桶越少描述越粗 |
| sampling-rate | 采样的数据比例,1 表示全量扫描 | 比例偏低时统计值会波动 |
| last-updated | 直方图生成时间 | 明显早于最近一次大批数据变更就该重建 |
| null-values | NULL 值在列中的占比 | 占比高时注意条件写法与估算口径 |
直方图与索引统计是两套东西:不带 UPDATE HISTOGRAM 的 ANALYZE TABLE 只刷新索引统计,不会顺带更新已有直方图;反之重建直方图也不等于刷新了索引的基数。两者都要顾,才能让估算稳定。
大表上生成直方图会做全表或抽样扫描,建议放在低峰期执行;桶数按列的区分度选,64 与 256 是常用档位,取值种类很少的列会自动退化成 singleton 形式,此时桶数参数不再起作用。
直方图不该被当成索引的替代:它无法减少要读的数据页,只让优化器算得更准。凡是索引能覆盖的过滤条件,优先考虑索引。