确认优化器选错索引需比对三组数字:rows预估扫描行数与实际COUNT(*)结果偏差是否超30%,filtered值是否极低(如

MySQL 选错索引不是 bug,是优化器基于过期/失真统计信息做的“合理误判”;强制纠正不能只靠 FORCE INDEX 硬上,得先确认它真错了、再选对干预方式。
怎么确认优化器真选错了索引
别光看 EXPLAIN 的 key 列,重点比三组数字:
-
rows(预估扫描行数)和你手动SELECT COUNT(*) WHERE ...的实际结果偏差是否超过 30%?偏差大说明统计失真 -
filtered值是否极低(比如 - 是否存在
Using filesort或Using temporary?结合ORDER BY和LIMIT时,优化器可能为避免排序而弃用更优索引(如order by id limit强制走主键却扫全表)
如果 EXPLAIN FORMAT=JSON 显示 used_range_access 为 false,且 rows_examined_per_scan 远高于实际命中行数,基本可断定选错。
FORCE INDEX 的写法和致命陷阱
语法必须严格:FORCE INDEX 要紧贴 FROM 表名后,不能塞到 WHERE 后面,也不能写错索引名——主键索引名固定是 PRIMARY(大写)。
- ✅ 正确:
SELECT * FROM orders FORCE INDEX (idx_user_status) WHERE user_id = 123; - ❌ 错误:
SELECT * FROM orders WHERE user_id = 123 FORCE INDEX (idx_user_status);(解析失败) - ❌ 错误:
SELECT * FROM orders FORCE INDEX (primary) WHERE ...(大小写敏感,报错ERROR 1176)
硬编码索引名会埋雷:后续删索引、改名、或某 user_id 数据量暴涨十倍,FORCE 反而让查询更慢,且无任何告警。
安装、修复、更新或打包一个基于 Python 和 PyMySQL 实现的 Windows `mysql` 命令。适用于用户希望在 PowerShell/CMD 中输入 `mysql` 时重定向到 Python 实现、需要交互式输入 MySQL 连接信息、需要同步到当前用户本地 bin 目录,或需要将该能力封装成可公开复用的 Codex skill。
比 FORCE INDEX 更稳的修复路径
优先用 ANALYZE TABLE 刷新统计信息,它直接修正优化器的“决策依据”:
- 对大表或刚批量导入数据后,
ANALYZE TABLE orders通常立竿见影 - 检查
information_schema.STATISTICS中CARDINALITY是否明显失真(如status只有 3 个值,但显示基数为 10000) - 生产环境避开高峰期执行;MySQL 8.0+ 默认轻量采样,5.7 及以前版本可能锁表
若 ANALYZE 无效,再查是否触发了隐式类型转换(如 WHERE user_id = '123' 但字段是 INT)或函数包裹(如 WHERE DATE(created_at) = ...)——这些情况下索引本就不能用,FORCE 也救不了。
复杂点常被忽略:直方图和驱动表顺序
MySQL 8.0+ 支持直方图,但它不替代索引,只帮优化器更准估算数据分布;对 city_id 这类倾斜字段有用,对布尔字段无效。
多表 JOIN 场景下,选错索引常是表连接顺序错误的副产品——这时该用 STRAIGHT_JOIN 固定驱动表,而不是在单表上 FORCE INDEX。它和 FORCE INDEX 完全不同层:一个管“谁先读”,一个管“怎么读”。

















