MySQL优化器因数据分布不均(如95%值相同)判断走索引比全表扫描慢,故type为ALL;直方图影响其行数估算,但不解决列间相关性问题。

为什么 WHERE 条件用了索引字段,但执行计划里还是 type: ALL
不是索引没建,也不是 SQL 写错,而是 MySQL 优化器判断“走索引比全表扫描还慢”。核心原因常是数据分布极端不均——比如某字段 95% 的值都是 'active',剩下 5% 才是其他值。这时哪怕加了索引,优化器看到直方图统计后,会直接放弃索引。
实操建议:
- 用
SHOW INDEX FROM table_name确认索引存在且状态正常(Index_type是 BTREE) - 查直方图是否启用:
SELECT * FROM information_schema.COLUMN_STATISTICS WHERE TABLE_NAME = 'your_table';(MySQL 8.0+) - 看实际值分布:
SELECT status, COUNT(*) FROM orders GROUP BY status ORDER BY COUNT(*) DESC;,如果头几个值占比超 80%,索引大概率被跳过 - 强制走索引仅作验证:
SELECT * FROM orders WHERE status = 'cancelled' USE INDEX (idx_status);,再对比EXPLAIN和真实执行时间
MySQL 8.0 直方图统计信息怎么生成和更新
直方图不是自动开启的,也不会随数据变更实时刷新。它是一次性采样生成的内存结构,只影响优化器对“某个值大概有多少行”的估算,不改变索引本身。
实操建议:
- 生成直方图需显式调用:
ANALYZE TABLE orders UPDATE HISTOGRAM ON status, created_at WITH 16 BUCKETS;(BUCKETS数量建议 16–256,太多反而拖慢优化器决策) - 直方图只对
VARCHAR、INT、DATE等可排序类型有效,JSON或TEXT不支持 - 更新频率取决于业务写入节奏:日增 10 万行以上,建议每天低峰期跑一次
ANALYZE TABLE ... UPDATE HISTOGRAM - 删直方图用:
ANALYZE TABLE orders DROP HISTOGRAM ON status;,别误删整个表统计
LIKE '%xxx' 为什么有时能用上索引,有时不能
关键不在通配符位置,而在优化器是否相信“匹配结果集足够小”。直方图在这里起决定作用:如果它告诉优化器 '%admin' 只会命中 3 行,就可能选索引;如果估算出 20 万行,就直接全表扫。
实操建议:
-
LIKE前导通配符本身不禁止索引使用,但 MySQL 只能在INDEX(abc)上做range扫描(而非ref),性能收益有限 - 检查直方图是否覆盖该列:
SELECT * FROM information_schema.COLUMN_STATISTICS WHERE COLUMN_NAME = 'username'; - 若直方图缺失或陈旧,
LIKE '%xxx'几乎必然退化为全表扫描 - 替代方案优先考虑全文索引(
FULLTEXT)或倒排表,别硬扛前缀模糊查
复合索引失效时,直方图会帮倒忙吗
会。直方图只统计单列值频次,完全不感知列间相关性。比如 INDEX(status, category),当 status='draft' 且 category='report' 实际只有 2 行,但直方图分别显示 status='draft' 占 40%、category='report' 占 30%,优化器就会高估交集行数,误判索引无效。
实操建议:
- 对高频组合查询字段,优先建复合索引,而不是依赖单列直方图 + 单列索引
- MySQL 8.0 不支持多列直方图,别指望它能修正复合条件估算偏差
- 用
EXPLAIN FORMAT=JSON查rows_examined_per_scan和filtered字段,对比直方图估算值与实际值差距 - 若发现
filtered长期远低于 10%,说明直方图失真严重,需重采样或调整查询逻辑
直方图不是银弹,它让优化器更“懂数据”,但也放大了单列统计的局限性。真正难处理的,永远是那些列值强相关、分布突变、或者写多读少的场景——这时候,与其调直方图,不如先看查询能不能拆、索引能不能换、数据模型要不要动。


















