直方图仅对无索引且值分布严重倾斜的列有效;需同时满足列在where/join中、无可用索引、数据分布不均三个条件才影响rows预估。

直方图只对无索引且严重倾斜的列有效
直方图不是“建了就快”,它只在三个条件同时满足时才可能影响 EXPLAIN 中的 rows 预估:列出现在 WHERE 或 JOIN 条件中、该列**没有可用索引**(包括主键、唯一键、前缀索引未命中)、值分布明显不均(如 status 列中 'active' 占 95%,其余状态总和不足 5%)。只要其中一条不成立,优化器会直接忽略直方图——比如你给主键列建直方图,ANALYZE TABLE 成功了,但执行计划纹丝不动。
建直方图前必须确认是否真缺索引
很多人一看到 type=ALL 就急着建直方图,结果白忙一场。先检查真实瓶颈:
- 用
SHOW INDEX FROM table_name确认目标列是否真的没索引; - 查
EXPLAIN FORMAT=JSON输出里的key和possible_keys,看优化器是否本可走索引却被绕过(比如用了函数包裹:WHERE UPPER(status) = 'CANCELLED'); - 如果列上有索引但没被选中,优先排查索引选择性差、统计信息过期、或
innodb_stats_persistent关闭导致采样不准——这些比直方图更常见、也更容易修复。
选对类型和桶数才能让优化器“看懂”数据
类型和桶数错配,直方图反而会让预估更糟:
- 低基数离散值(如
status、gender、枚举字段)→ 必须用SINGLETON类型,桶数设为实际唯一值数量 ±20%;例如有 12 种状态,用WITH 15 BUCKETS;不指定TYPE默认是EQ_HEIGHT,会导致频次丢失; - 高基数连续值(如
created_at、user_id)→ 用EQ_HEIGHT,桶数控制在 100–256;超过 1000 会拖慢查询解析,且information_schema.COLUMN_STATISTICS表体积激增; - 字符串列平均长度 > 200 字节 → 直接建可能报
ER_TOO_LONG_STRING;可临时截断:ANALYZE TABLE t UPDATE HISTOGRAM ON SUBSTRING(status, 1, 100) WITH 20 BUCKETS,但要注意精度损失。
验证是否真正生效不能只看命令返回
Query OK 不代表直方图起作用,必须交叉验证两处:
- 查元数据:
SELECT HISTOGRAM FROM information_schema.COLUMN_STATISTICS WHERE TABLE_NAME = 't' AND COLUMN_NAME = 'status',返回非空 JSON 才算写入成功; - 对比执行计划变化:对低频值(如
WHERE status = 'shipped')执行EXPLAIN FORMAT=JSON,重点看query_block->condition_filtering_probability是否显著提升(如从 0.001 升到 0.005),以及rows是否收敛到真实值附近; - 注意触发条件:只有当预估偏差超 10 倍(如预估 1 行、实际扫 30 万行)时,直方图才可能介入;偏差小,优化器认为现有统计已够用。
最常被忽略的一点:直方图不会自动更新。数据持续写入后,旧直方图可能迅速失效,尤其在高频变更表上。别依赖它长期有效,得结合业务节奏定期重跑 ANALYZE TABLE ... UPDATE HISTOGRAM,且尽量安排在低峰期——否则会引发元数据锁,卡住 DML。











