统计信息失真会导致优化器跳过索引;需查Cardinality或reltuples验证,误差超20%应执行ANALYZE TABLE或VACUUM ANALYZE;若仍不走索引,则检查字段类型、字符集、复合索引顺序是否匹配。

EXPLAIN 显示 type=ALL,但索引明明存在
这种情况不是索引没建,而是优化器压根没选它。MySQL/PostgreSQL 选执行计划时依赖统计信息估算成本,如果 information_schema.STATISTICS 或 pg_class.reltuples 里的行数、基数(Cardinality)严重失真,优化器会误判“走索引比全表扫描还慢”,直接跳过索引。
常见诱因包括:
- 表刚批量导入或清空重建,但没执行
ANALYZE TABLE(MySQL)或VACUUM ANALYZE(PostgreSQL) - 大表长期未更新统计信息,而实际数据分布已剧烈变化(比如 status 字段从 90% 'active' 变成 70% 'archived')
- 使用了分区表,但只对主表
ANALYZE,没逐个分析子分区
如何快速验证统计信息是否过期
别猜,直接查。MySQL 下运行:
SHOW INDEX FROM your_table;
重点看 Cardinality 列 —— 如果它远低于实际行数(例如表有 500 万行,Cardinality 显示 1200),说明统计信息失效。PostgreSQL 则查:
SELECT relname, reltuples::BIGINT AS rowcount FROM pg_class WHERE relname = 'your_table';
对比你心里的行数,误差超过 20% 就该刷新了。
刷新统计信息后仍不走索引?再盯三处
统计信息只是基础,它救不了以下硬伤:
-
JOIN条件字段类型不一致,比如左表是VARCHAR(32),右表是CHAR(32),即使都建了索引,也会触发隐式转换,让索引失效 - 两表字符集不同,如一个用
utf8mb4,另一个用utf8,JOIN 时 MySQL 自动加CONVERT(),等价于在索引列上用了函数 - 复合索引顺序和
ON条件字段顺序不匹配,例如ON a.x = b.x AND a.y = b.y,但 b 表索引是(y, x)而非(x, y)
强制刷新不是万能解药
执行 ANALYZE TABLE 后,如果 EXPLAIN 的 key 列仍是 NULL,或 rows 接近总行数,说明问题不在统计信息本身,而在 SQL 写法或索引设计。此时要立刻检查 EXPLAIN 的 Extra 字段:出现 Using join buffer (Block Nested Loop) 是明确信号——优化器已经放弃索引驱动,改用低效嵌套循环。










