直方图仅在同时满足“无可用索引或未命中前导列”“列值分布严重倾斜”“列用于where/join/group by且需估算选择率”三个条件时才被优化器采纳;必须手动创建、验证并定期维护,否则可能恶化执行计划。

ANALYZE TABLE 执行成功 ≠ 执行计划变好。直方图只在特定条件下被优化器采纳,且必须手动创建、验证、维护;盲目加直方图反而可能让 EXPLAIN 中的 rows 预估更离谱。
直方图生效的三个硬性前提
优化器根本不会读直方图,除非同时满足以下全部条件:
- 目标列上没有可用索引,或虽有索引但查询未命中前导列(例如复合索引
(status, created_at),却只查WHERE created_at > '2025-01-01') - 该列值分布严重不均(如
status中'active'占 97%,其余状态合计仅 3%) - 该列出现在
WHERE、JOIN或GROUP BY条件中,且优化器需估算选择率(selectivity)来决定访问路径或连接顺序
只要缺一,EXPLAIN FORMAT=JSON 里的 "rows" 就不会变化,直方图等于白建。
ANALYZE TABLE UPDATE HISTOGRAM 的实操要点与常见错误
语法看着简单,参数错一个就失效:
- 必须显式指定
WITH N BUCKETS,不写默认用 100 —— 对小表浪费资源,对大表桶太少失真;常用起点是 16(低基数)或 100(高基数) - 多列要分别写:正确写法是
ANALYZE TABLE orders UPDATE HISTOGRAM ON status WITH 25 BUCKETS, UPDATE HISTOGRAM ON priority WITH 16 BUCKETS;,不能简写成ON status, priority - 执行需
SELECT和INSERT权限;若启用了innodb_read_only=ON,直接报错ERROR 1290 (HY000) - 执行期间持表级只读锁,长事务未结束时会被阻塞 —— 务必避开业务高峰
选错 TYPE 比不建还危险
MySQL 支持 SINGLETON 和 EQ_HEIGHT 两种类型,选错会让优化器“更迷糊”:
-
SINGLETON:适合低基数、离散明确的列(如枚举型order_status),每个唯一值单独记频次;桶数建议设为实际唯一值数量 ±20% -
EQ_HEIGHT:适合高基数、连续值列(如created_at、amount),把值域切分为等频区间;桶数建议 100–256,超过 1000 显著拖慢解析 - 不显式指定
TYPE默认用EQ_HEIGHT—— 对只有 4 个状态的列,这大概率导致频次丢失 - 字符串列平均长度 > 200 字节时,建直方图可能报
ER_TOO_LONG_STRING,需先SUBSTRING(col, 1, 100)截断
怎么确认直方图真被用了?
不能只看命令是否返回 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 = 'cancelled';,对比"rows"字段变化(如从 12000 → 380)才说明被采纳 - 注意陷阱:如果查询写了
WHERE UPPER(status) = 'CANCELLED'或WHERE status + 0 = '1',直方图完全失效
最常被忽略的一点:直方图不是“建一次就永久有效”。数据分布偏移后,旧直方图会误导优化器选错驱动表或访问路径,定期验证和清理比盲目堆砌更重要。











