EXPLAIN 显示 type: ALL 或 key: NULL 通常因优化器基于统计估算全表扫描更快,原因包括数据倾斜、统计过期、索引列顺序不满足最左前缀、函数/隐式转换导致索引失效,或 FORCE INDEX 使用不当;应先 ANALYZE TABLE,再验证索引设计与查询条件匹配性。

EXPLAIN 显示 type: ALL 或 key: NULL,但你知道有匹配索引
这通常不是优化器“犯傻”,而是它基于统计信息估算后认为全表扫描更快——比如某字段值占比超 95%,或 ANALYZE TABLE 长期没更新导致行数预估严重偏低(如预估 12 行,实际 200 万)。此时加 FORCE INDEX 是验证手段:如果强制后 EXPLAIN 的 key 变成目标索引、rows 显著下降,说明问题出在统计不准或数据倾斜,而非 SQL 写法或索引设计。
- 先执行
ANALYZE TABLE tbl_name,再看是否改善;若无变化,再试FORCE INDEX - 确认索引列顺序和 WHERE 条件是否满足最左前缀:索引是
(a, b, c),但查询只写WHERE b = 1 AND c > '2024'→ 强制也无效 -
EXPLAIN FORMAT=JSON查看filtered字段,若低于 10%,优化器弃用索引大概率合理
WHERE 中用了函数或隐式转换,FORCE INDEX 仍不生效
强制索引不能修复索引失效的根本原因。例如 WHERE YEAR(created_at) = 2024 或 WHERE user_id = '123'(user_id 是 BIGINT),MySQL 无法在索引上直接比对,FORCE INDEX (created_at) 或 FORCE INDEX (PRIMARY) 都会被静默忽略。
- 检查
EXPLAIN的Extra字段:出现Using where; Using index是正常走索引;若只有Using where,大概率存在隐式转换或函数 - 用
SHOW WARNINGS看优化器是否提示 “Cannot use ref access on index … due to type mismatch” - 把
WHERE user_id = '123'改成WHERE user_id = 123,再试强制
多表 JOIN 场景下 FORCE INDEX 写错位置或漏声明
FORCE INDEX 必须紧贴每个表名(或别名)之后,不能放在 JOIN 关键字后、也不能混在 ON 或 WHERE 里。漏掉任一表的声明,该表就仍由优化器自由选索引。
- 正确写法:
SELECT * FROM users u FORCE INDEX (PRIMARY) JOIN orders o FORCE INDEX (idx_user_id_status) ON u.id = o.user_id - 错误写法:
FROM users u JOIN orders o ON u.id = o.user_id FORCE INDEX (idx_user_id_status)→ 对orders无效 - 若用视图或子查询,
FORCE INDEX在外层无效,必须下推到视图定义或改用临时表
加了 FORCE INDEX 却变慢,为什么没人告诉你这个风险
强制索引绕过了优化器的成本模型,等于把执行计划“钉死”。一旦数据分布突变(比如 status = 'pending' 的记录从 1 万暴涨到 800 万),原本高效的 idx_status_created 就会引发海量回表,而优化器本可能在新统计下自动切到覆盖更好的索引或改用全表扫描。
- 线上只建议用于紧急止损,且必须搭配监控:对比强制前后
Handler_read_*和Slow_queries指标 - 不要在业务高峰期长期使用;更稳妥的做法是补
ANALYZE TABLE+ 调整索引顺序(如把高区分度列前置) -
FORCE INDEX不影响索引合并(index merge),也不保证覆盖扫描——是否回表,只取决于你查的字段和索引结构本身


















