直方图仅提升WHERE单列过滤的基数估算精度,不改索引、不优化JOIN/GROUP BY;须确认列倾斜、无有效索引且HISTOGRAM为空才建,桶数依类型设20–512,函数包装或分区未命中会使其失效。

MySQL 8.0 的执行计划变差,不是直方图“没用”,而是它根本没被用上——或者压根没建、建错了列、被函数拦在门外。
直方图只影响基数估算,不改索引也不重写 SQL
直方图的作用非常具体:仅在优化器估算 WHERE 条件匹配多少行时提供更准的数字(比如预估 1 行 vs 实际扫描 5 万行)。它不会自动建索引,不会让 ORDER BY 突然走索引,也不会绕过 filesort。如果你的慢查询主因是缺失索引或 max_length_for_sort_data 被废弃(8.0.20+ 已移除),加直方图毫无意义。
- 检查
EXPLAIN中的rows字段是否明显偏离实际(>10 倍);只有这时直方图才可能起作用 - 若
type是ALL或index,优先补索引,别先折腾直方图 - 直方图对
JOIN条件列、GROUP BY列无效,只对单表过滤条件列有效
建直方图前必须验证三个前提
盲目 ANALYZE TABLE ... UPDATE HISTOGRAM 可能白忙活,甚至误导优化器。先确认:
- 该列确实分布严重倾斜(如
status:95%'active',其余十几种状态各占零点几 %) - 该列出现在
WHERE子句中,且当前没有可用索引(或有索引但优化器因统计不准弃用了) -
information_schema.COLUMN_STATISTICS表中对应行的HISTOGRAM字段为空(说明还没建)
命令示例:SELECT HISTOGRAM FROM information_schema.COLUMN_STATISTICS WHERE TABLE_SCHEMA = 'db' AND TABLE_NAME = 'orders' AND COLUMN_NAME = 'status';
UPDATE HISTOGRAM 的桶数(BUCKETS)怎么设才不翻车
桶数不是越多越好,100 是安全起点,但需按列类型调整:
- 枚举/状态类字段(如
status、order_type):20–50 桶足够,覆盖所有常见值即可 - 高基数数值列(如
user_id、created_at):100 桶易失效,可试 256 或 512,但超过 1000 不推荐 - utf8mb4 字符串列平均长度 > 200 字节:建之前先
SUBSTRING(col, 1, 100)截断,否则可能报ER_TOO_LONG_STRING - 建完立刻验证:
ANALYZE TABLE orders;(注意:这不是更新直方图,只是刷新基础统计)
直方图生效了,但查询还是慢?排查这三个盲区
直方图数据存在 information_schema.COLUMN_STATISTICS,但它很容易被绕过:
- WHERE 条件里用了函数,如
WHERE UPPER(status) = 'ACTIVE'—— 直方图基于原始列值,无法参与估算 - 表是分区表,且查询条件未明确命中分区(如没写
PARTITION(p2024)),优化器可能忽略直方图(8.0.19+ 修复部分情况,但仍有漏) - 直方图建在从库,而
information_schema.COLUMN_STATISTICS不复制,导致主从EXPLAIN结果不一致
最常被忽略的一点:直方图只在优化器“犹豫”时起作用——当有强索引可用时,它根本不会看直方图。所以别指望它救一个没索引的 WHERE + ORDER BY 查询,那得先加索引,再考虑直方图补统计精度。


















