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

直方图不能让无索引列“变出索引”,它只让优化器对 WHERE 条件中该列的过滤比例(filtered)估算更准——预估准了,才可能避免错误选择 type: ALL,或在多表 JOIN 中选对驱动表。
为什么给无索引列建直方图后 EXPLAIN 还是 type: ALL
直方图不改变访问方式,只修正行数预估。即使 filtered 从 33.33% 变成 0.4%,只要该列没索引,MySQL 依然只能全表扫描;但这个更准的预估会影响后续决策,比如:是否物化子查询、用哪个表做外连接驱动表、是否提前终止排序等。常见误判点包括:
-
WHERE UPPER(status) = 'ACTIVE'—— 函数包裹导致直方图完全失效,优化器看到的是表达式结果,不是原始列 - 字段类型为
JSON或压缩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不动,可能是该查询还受其他条件干扰(如 JOIN 顺序、外部 LIMIT),直方图影响被稀释
最易被忽略的一点:直方图不会自动更新。数据分布变了,旧直方图就成了负资产——它可能让优化器比瞎猜还错。定期用 ANALYZE TABLE t UPDATE HISTOGRAM ON col; 刷新,比建的时候更关键。











