直方图仅在无索引且数据倾斜时生效:需满足列用于WHERE/JOIN、无可用索引、值分布严重不均三个条件;否则rows预估不变,直方图无效。

直方图只在没索引且数据倾斜时才起作用
直方图不是“万能加速器”,它只对满足三个条件的列生效:该列出现在 WHERE / JOIN 条件中、没有可用索引(或条件未命中索引前导列)、值分布严重不均(比如 status 列中 95% 是 'active',其余十几种状态加起来才占 5%)。只要有一条不满足,EXPLAIN 中的 rows 预估就不会变,直方图等于白建。
常见误用场景:
- 给已有主键或唯一索引的列建直方图——优化器优先用索引统计,直方图被忽略
- 给
WHERE UPPER(col) = 'X'这类带函数的条件列建——优化器看到的是表达式结果,无法关联原始列直方图 - 在分区表上建了直方图但查询没指定具体分区——MySQL 8.0.19+ 修复部分情况,但仍有大量边界失效
选错类型或桶数会让优化器更迷糊
SINGLETON 和 EQ_HEIGHT 不是随便挑的。类型选错,轻则精度下降,重则元数据膨胀、解析变慢。
判断依据很简单:
- 低基数离散值(如
status、order_type、布尔字段)→ 用SINGLETON,桶数设为唯一值数量 ±20%,例如 18 种状态就用WITH 22 BUCKETS - 高基数连续值(如
created_at、user_id、金额)→ 用EQ_HEIGHT,桶数控制在 100–256;超过 1000 会显著拖慢查询解析,且information_schema.COLUMN_STATISTICS表体积激增 - 不指定
TYPE时默认EQ_HEIGHT,对枚举列大概率误判,导致频次信息丢失
字符串列若平均长度 > 200 字节,直接建直方图可能报 ER_TOO_LONG_STRING;可临时截断:ANALYZE TABLE t UPDATE HISTOGRAM ON SUBSTRING(status, 1, 100) WITH 20 BUCKETS;,但要注意精度损失。
建完不等于生效,必须两步验证
执行 ANALYZE TABLE t UPDATE HISTOGRAM ON col; 成功,只代表语句没报错,不代表优化器读取并用了它。
真正验证要分两步:
- 查元数据:
SELECT HISTOGRAM FROM information_schema.COLUMN_STATISTICS WHERE TABLE_NAME = 't' AND COLUMN_NAME = 'col';—— 返回非空 JSON 才算写入成功 - 比预估:
EXPLAIN FORMAT=JSON SELECT * FROM t WHERE col = 'x';,对比建直方图前后"rows"字段变化;从 12000 → 380 这种量级差异才算被采纳
注意:直方图不会把 type: ALL 变成 type: ref,它只影响多个可行路径之间的代价权衡——比如让优化器在“全表扫描”和“走某个二级索引再回表”之间,选对那个实际更快的。
直方图必须手动更新,且不能替代索引
直方图不会自动刷新,也不会随 INSERT/UPDATE 自动维护。业务写入频繁、数据分布明显偏移后(比如新上线一种订单状态),就得人工触发 ANALYZE TABLE ... UPDATE HISTOGRAM。
但它解决不了根本问题:
- 它不能替代索引——高频查询字段该建索引还得建
- 它不降低 I/O——只是帮优化器少走弯路,不是减少磁盘读
- 它有开销——大表分析耗 CPU 和 I/O,建议在低峰期操作
最容易被忽略的一点:直方图元数据(存于 information_schema.COLUMN_STATISTICS)默认不复制到从库;如果只在从库建了直方图,主库同步过来的查询在从库 EXPLAIN 看着好,实际执行却按主库旧统计跑,结果不一致。


















