MySQL基于代价估算自动选择全表扫描或索引扫描,当索引过滤性差、回表成本高、统计信息过期、索引列被函数操作或隐式转换时,会退化为全表扫描;应通过EXPLAIN分析代价,优先优化索引设计而非强制走索引。
MySQL 选哪种扫描方式,不是由 SQL 写法“决定”的,而是优化器基于 cost(代价估算)自动选择的。它会预估:走索引要读多少页 + 回表多少次 + 排序/临时表开销,对比全表扫描读多少页,取总代价更低的路径。所以即使你加了索引,EXPLAIN 仍可能显示 type: ALL(全表扫描)。
常见诱因不是“索引坏了”,而是优化器判断走索引反而更慢:
WHERE 条件匹配行数太多(比如 status != 'done' 覆盖 85% 的数据),索引过滤性差,回表成本高SELECT * 或含非索引列),导致大量随机 I/O 回表ANALYZE TABLE 没更新),优化器误判索引选择率WHERE YEAR(create_time) = 2025),导致索引
WHERE user_id = '123',而 user_id 是 INT),触发全字段扫描先看执行计划,再针对性调整:
EXPLAIN FORMAT=JSON 查看 query_cost 和 used_columns,比普通 EXPLAIN 更清楚代价来源SELECT ... FROM t USE INDEX (idx_name) WHERE ...,但上线禁用——掩盖问题而非解决WHERE、ORDER BY、SELECT 中用到的列都放进一个复合索引,避免回表(例如 INDEX (a, b, c) 支持 WHERE a=1 ORDER BY b LIMIT 10)gender、is_deleted)单独建索引意义极小,应组合进复合索引并前置高区分列EXPLAIN FORMAT=JSON SELECT id, name FROM users WHERE city = 'Beijing' AND age > 30;
索引扫描 ≠ 快,全表扫描 ≠ 慢。InnoDB 全表扫描是顺序 I/O,而索引扫描+回表是大量随机 I/O;当数据量小(比如几千行)、缓冲池充足时,全表扫描可能更快。真正要盯的是 rows 预估是否严重偏离实际,以及 key 是否为空——那说明优化器根本没考虑可用索引,得查是否写法触发了索引失效规则。