直方图需手动创建、刷新和验证才生效,否则仅写入元数据而优化器不使用;三类列不应建直方图:已有索引首列、分布均匀列、JSON/TEXT/函数包裹列。

直方图不是升级完就自动生效的补丁,它必须手动创建、显式刷新、针对性验证——否则 ANALYZE TABLE ... UPDATE HISTOGRAM 执行成功也只是往 information_schema.COLUMN_STATISTICS 里写了一段 JSON,优化器根本不会看。
哪些列值得建直方图?别碰这三类字段
直方图只对特定数据分布+特定查询模式起作用,建错列等于白干:
- 已有索引的列(尤其是复合索引的首列):优化器优先走索引,直方图不参与决策
- 值分布均匀的列(如
user_id、created_at):默认统计已够用,直方图无法带来预估提升 - JSON、
TEXT、压缩字段或函数包裹列(如WHERE YEAR(created_at) = 2025):MySQL 当前版本不支持,语句静默跳过
建直方图前必须跑 ANALYZE TABLE,否则桶里全是过期数据
直方图基于采样构建,而采样依赖当前表级统计信息。如果上次 ANALYZE TABLE 是半年前,那直方图描述的是旧数据分布,反而会误导优化器:
- 先执行
ANALYZE TABLE orders;,确保mysql.innodb_table_stats和mysql.innodb_index_stats是新鲜的 - 再执行
ANALYZE TABLE orders UPDATE HISTOGRAM ON status WITH 25 BUCKETS; - 避免在高峰期执行,大表分析可能触发元数据锁,阻塞
INSERT/UPDATE
WITH N BUCKETS 怎么设才不翻车?看列类型选数字
桶数不是越多越好,设错会导致元数据膨胀或精度反降:
- 低基数枚举列(如
status只有'pending','shipped','failed'等 10 个值)→ 用SINGLETON类型:ANALYZE TABLE orders UPDATE HISTOGRAM ON status WITH 12 BUCKETS TYPE SINGLETON; - 高基数连续值(如
amount、created_at)→ 用默认EQ_HEIGHT,桶数控制在100–256;超过1000会让 SQL 准备阶段变慢 - VARCHAR 超长字段(平均长度 > 200 字节)→ 直接建可能报
ER_TOO_LONG_STRING,改用截断:ANALYZE TABLE orders UPDATE HISTOGRAM ON SUBSTRING(status, 1, 100) WITH 20 BUCKETS;(注意精度损失)
验证是否真起效?只看这两处,别信 “Query OK”
命令返回成功只是入库完成,不代表优化器采纳了它:
- 查元数据落地:
SELECT HISTOGRAM FROM information_schema.COLUMN_STATISTICS WHERE TABLE_NAME = 'orders' AND COLUMN_NAME = 'status';→ 必须返回非空 JSON,否则直方图没存进去 - 查优化器是否使用:
EXPLAIN FORMAT=JSON SELECT * FROM orders WHERE status = 'failed';→ 对比"filtered"字段:建之前是10.00,建之后降到0.42才算真正生效 - 注意:
rows预估值可能不变(尤其无索引时仍是type: ALL),但filtered变了,说明代价估算已更新,JOIN 顺序、物化子查询等后续决策才可能被修正
最易忽略的一点:直方图从不改变访问类型(比如不会把 ALL 变成 ref),它只影响优化器在“已有选项”中挑哪条路更省——如果唯一选项就是全表扫描,再准的直方图也救不了。真正该优先做的,永远是给高频 WHERE 列加索引。


















