mysql执行过程中如何选择扫描方法(全表扫描、索引扫描)

来源:这里教程网 时间:2026-02-28 20:44:45 作者:

MySQL 为什么有时走全表扫描,有时走索引扫描

MySQL 选哪种扫描方式,不是由 SQL 写法“决定”的,而是优化器基于

cost
(代价估算)自动选择的。它会预估:走索引要读多少页 + 回表多少次 + 排序/临时表开销,对比全表扫描读多少页,取总代价更低的路径。所以即使你加了索引,
EXPLAIN
仍可能显示
type: ALL
(全表扫描)。

什么情况下 MySQL 会放弃索引,退化为全表扫描

常见诱因不是“索引坏了”,而是优化器判断走索引反而更慢:

WHERE
条件匹配行数太多(比如
status != 'done'
覆盖 85% 的数据),索引过滤性差,回表成本高
查询列太多,且未被覆盖索引包含(
SELECT *
或含非索引列),导致大量随机 I/O 回表
统计信息过期(
ANALYZE TABLE
没更新),优化器误判索引选择率
使用函数或表达式操作索引列(如
WHERE YEAR(create_time) = 2024
),导致索引失效(无法用 B+ 树快速定位)
隐式类型转换(如
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
是否为空——那说明优化器根本没考虑可用索引,得查是否写法触发了索引失效规则。

相关推荐