直方图仅在无索引或未命中索引前导列、值分布严重不均、且用于WHERE/JON/GROUP BY选择率估算时生效;需手动维护,过期或冗余应删除。

直方图不是“开了就快”,它只在优化器真正需要列值分布信息、且该列又没索引或不是索引前导列时才起作用;盲目加直方图反而可能让优化器误判,尤其当数据变动频繁而直方图未及时更新时。
直方图真正起效的三个前提
直方图不会自动提升所有查询性能,它只在特定条件下被优化器采纳:
- 目标列上没有可用索引,或虽有索引但查询条件未命中索引前导列(例如复合索引
(status, created_at),却只查WHERE created_at > '2025-01-01') - 该列值分布严重不均匀(比如
status列中'pending'占 95%,'done'和'failed'各占 2.5%) - 查询中该列出现在
WHERE、JOIN或GROUP BY条件里,且优化器需要估算选择率(selectivity)来决定是否走全表扫描、用哪个表做驱动表等
ANALYZE TABLE UPDATE HISTOGRAM 的实际写法与坑点
语法看着简单,但参数错一个就可能白跑或效果打折:
-
ANALYZE TABLE orders UPDATE HISTOGRAM ON status WITH 25 BUCKETS;是标准写法;省略WITH N BUCKETS会用默认 100,对小表浪费资源,对大表可能桶太少失真 - 不能写成
ANALYZE TABLE orders UPDATE HISTOGRAM ON status, priority;—— 多列必须显式重复UPDATE HISTOGRAM ON,正确写法是: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): The MySQL server is running with the --innodb-read-only option so it cannot execute this statement - 执行期间会持有一个表级只读锁(
read lock),长事务未结束时会被阻塞,建议避开业务高峰
怎么确认直方图真的被用了?
别光看语句执行成功,得验证优化器是否实际参考了它:
- 查直方图是否存在:
SELECT * FROM information_schema.COLUMN_STATISTICS WHERE SCHEMA_NAME = 'your_db' AND TABLE_NAME = 'orders' AND COLUMN_NAME = 'status';—— 返回非空即已创建,HISTOGRAM字段是 JSON,可读性差但至少存在 - 对比执行计划:先用
EXPLAIN FORMAT=JSON SELECT * FROM orders WHERE status = 'done';记下"rows"预估行数;建完直方图后重跑,如果预估从 10000 变成 240(假设真实占比 2.4%),说明被采纳了 - 注意陷阱:如果该列上有索引且查询能走索引,优化器通常优先用索引统计而非直方图,此时直方图不会参与决策
什么时候该删掉直方图?
直方图不是一劳永逸的,过期或冗余的直方图反而干扰优化器:
- 数据分布发生根本变化(比如原
status从 95% pending 变成各状态均分),旧直方图会误导优化器选错连接顺序或执行路径 - 发现某列直方图建了但从未在
EXPLAIN中体现影响(连续几次关键查询计划都未变),大概率是场景不匹配,删掉省空间 - 删除命令是:
ANALYZE TABLE orders DROP HISTOGRAM ON status;—— 注意不是DROP STATISTICS,也不是删整个表统计 - 直方图不随 DML 自动更新,所以高写入表(如日志表)建议每月或每次大批次导入后手动重建一次
最常被忽略的一点:直方图只影响优化器的「估算」,不改变实际执行逻辑。哪怕预估行数准了,如果底层没索引、I/O 还是多,查询照样慢。它解决的是「选错路」的问题,不是「走路慢」的问题。


















