直方图不能让无索引列跳过全表扫描,它只提升优化器对WHERE条件过滤比例(filtered)的估算精度,从而影响JOIN驱动表选择等决策,但不改变type=ALL的全表扫描本质。

直方图不能让无索引列跳过全表扫描,它只让优化器更准地估算 WHERE 条件能过滤掉多少行——这个“估算值”变了,执行计划才可能跟着变,比如在 JOIN 中选对驱动表、避免物化子查询、提前终止排序等。
为什么加了直方图,EXPLAIN 还是 type: ALL
这是最常被误解的点。type: ALL 表示访问方式没变,和直方图无关;直方图只影响优化器内部的行数预估(rows)和过滤比例(filtered),不改变物理扫描逻辑。
常见绕过直方图的情况包括:
-
WHERE UPPER(status) = 'ACTIVE':函数包裹后,优化器看到的是表达式结果,不是原始列,直方图失效 - 字段类型为
JSON、GEOMETRY或压缩的TEXT:MySQL 当前版本不支持为其建直方图,语句静默跳过 - 查询涉及分区表,但条件未精确匹配分区键(如按
region分区,却查WHERE created_at > '2025-01-01'):直方图元数据不会加载 - 建完直方图没执行
ANALYZE TABLE t;:优化器默认不自动重载,必须显式触发统计刷新
如何选对直方图类型和桶数
类型和桶数选错,直方图反而会误导优化器。关键看列值分布特征,不是凭感觉:
- 低基数离散值(如
status、order_type、布尔字段)→ 必须用SINGLETON类型,桶数设为唯一值数量 ±20%。例如 12 种状态,写WITH 14 BUCKETS TYPE SINGLETON - 高基数连续值(如
created_at、user_id、金额)→ 用EQ_HEIGHT(MySQL 默认),桶数控制在 100–256。超过 1000 会拖慢查询解析,且information_schema.COLUMN_STATISTICS表体积激增 - 字符串列平均长度 > 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_SCHEMA = 'db' AND TABLE_NAME = 'orders' AND COLUMN_NAME = 'status';—— 返回非空 JSON 才算落地成功 - 对比
EXPLAIN FORMAT=JSON输出中的"filtered"字段:对低频值(如WHERE status = 'failed')执行前后对比,若从10.00→0.37,说明优化器已采纳 - 注意观察
"rows"是否同步变化:如果filtered变了但rows没变,大概率是没触发统计重载,需补一次ANALYZE TABLE orders;
直方图真正起效的前提,是列值分布严重倾斜 + 无索引 + 频繁出现在 WHERE 中。一旦字段上了索引,或值分布均匀,直方图基本不会改变执行计划——优化器优先信索引统计,直方图只是备选参考。最容易被忽略的,是建完不刷统计、函数包裹绕过、以及误以为它能替代索引。


















