MySQL 8.0直方图需显式创建且不随ANALYZE TABLE自动更新,必须用UPDATE HISTOGRAM ON指定列和桶数,并根据列特性选择SINGLETON或EQ_HEIGHT类型,仅在无有效索引或索引未命中时影响优化器基数估算。

直方图不是自动生效的,必须显式创建
MySQL 8.0 的直方图不会随 ANALYZE TABLE 自动产生。哪怕你每天跑十次 ANALYZE TABLE orders,只要没带 UPDATE HISTOGRAM ON,就只更新基础统计(行数、平均长度等),对非均匀分布列毫无帮助。
- 正确写法是:
ANALYZE TABLE orders UPDATE HISTOGRAM ON status WITH 25 BUCKETS; - 多列要分别指定:
ANALYZE TABLE orders UPDATE HISTOGRAM ON status WITH 25 BUCKETS, UPDATE HISTOGRAM ON priority WITH 16 BUCKETS; - 执行需
SELECT和INSERT权限;若启用了innodb_read_only=ON,会直接报错ERROR 1290 (HY000) - 执行期间持表级只读锁,长事务未结束时会被阻塞,建议避开业务高峰
选错直方图类型会让优化器更迷糊
MySQL 支持 SINGLETON 和 EQ_HEIGHT 两种类型,选错反而导致估算偏差更大。
-
SINGLETON:适合低基数、离散值明确的列(如状态枚举),每个唯一值单独记录频次 -
EQ_HEIGHT:适合高基数、连续分布列(如时间戳、金额),把值域切分成等频区间 - 用
EQ_HEIGHT处理只有 3 个值的状态列,会把 “completed”“pending”“cancelled” 塞进几个桶,丢失精确频次 → 优化器可能高估 “pending” 数量,错误选择索引扫描 - 不显式指定类型,默认是
EQ_HEIGHT,务必根据列特性加TYPE SINGLETON或TYPE EQ_HEIGHT
直方图只在特定条件下被优化器采纳
它不是万能加速器,只在优化器真正需要列值分布信息、且该列又没索引或不是索引前导列时才起作用。
- 列上有可用索引且查询命中前导列(如复合索引
(status, created_at)中查WHERE status = 'done')→ 直方图基本不参与估算 - 列出现在
WHERE中但用了函数包裹,如WHERE YEAR(created_at) = 2025或UPPER(status) = 'ACTIVE'→ 直方图失效 - 列用于
JOIN但存在隐式类型转换(orders.user_id BIGINT关联users.id VARCHAR)→ 优化器退回到粗略基数估计 - 数据变动频繁而直方图未及时更新 → 可能误导优化器,尤其当真实分布已偏移时
验证直方图是否真被用了,不能只看命令是否成功
执行完 ANALYZE TABLE ... UPDATE HISTOGRAM 后,得验证优化器是否实际参考了它。
- 查元数据:
SELECT HISTOGRAM FROM information_schema.COLUMN_STATISTICS WHERE TABLE_NAME = 'orders' AND COLUMN_NAME = 'status';—— 返回非空 JSON 才算成功写入 - 对比执行计划:
EXPLAIN FORMAT=JSON SELECT * FROM orders WHERE status = 'done';,建直方图前后比对"rows"预估行数变化 - 注意陷阱:如果该列有索引,
EXPLAIN的访问类型(type)可能仍是ref或range,但预估行数变准了,这才是直方图生效的核心信号 - 从库上建了直方图没用——
information_schema.COLUMN_STATISTICS不复制,主从行为可能不一致
直方图的价值不在“让某条 SQL 变快”,而在让优化器在连接顺序、驱动表选择、是否走全表扫描这些关键决策点上“猜得更准”。最容易被忽略的是:它只影响基数估算,不改变索引结构、不重写 SQL、也不降低 I/O 次数——误以为建了就等于加了索引,是踩坑最深的地方。


















