mysql中的全表扫描与索引优化技巧

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

为什么
EXPLAIN
显示
type=ALL
就该警惕

这表示 MySQL 正在执行全表扫描,即逐行读取整张表来匹配条件。哪怕只有几万行,一旦并发上来或字段含大文本,响应就会明显变慢。常见诱因是

WHERE
条件列没索引、用了函数(如
WHERE YEAR(created_at) = 2023
)、或隐式类型转换(比如对字符串字段传数字值)。

EXPLAIN FORMAT=TREE
查看更直观的执行路径,注意
rows
filtered
的乘积是否远超实际返回行数
避免在索引列上使用
!=
NOT IN
LIKE '%abc'
—— 这些基本无法走索引
联合索引要注意最左前缀:如果建了
(a, b, c)
,那
WHERE b = ?
WHERE c = ?
是不会命中索引的

如何判断该加单列索引还是联合索引

单列索引适合高频独立查询的字段,比如

user_id
status
;联合索引则要按「查询频率高 + 过滤性强 + 出现在
WHERE
最左侧」的顺序排列。MySQL 8.0+ 支持降序索引,但
ORDER BY a ASC, b DESC
需显式声明
(a ASC, b DESC)
才能完全覆盖排序。

优先把等值条件列放前面,范围查询(
>
,
BETWEEN
)放后面,
ORDER BY
列尽量接在最后
例如查询
SELECT * FROM orders WHERE shop_id = ? AND status = ? ORDER BY created_at DESC
,推荐索引为
(shop_id, status, created_at)
别盲目给所有
WHERE
字段都建索引——索引越多,写入越慢,且优化器可能选错执行计划

SELECT *
和覆盖索引的关系

如果一个查询的所有字段都能从索引中直接获取(即「覆盖索引」),MySQL 就不必回表查聚簇索引,性能提升显著。但前提是这些字段必须全部出现在同一个索引的「叶子节点」里。

例如有索引
(user_id, email, created_at)
,那么
SELECT user_id, email FROM users WHERE user_id = ?
就是覆盖索引查询
SELECT *
几乎不可能被覆盖,因为主键以外的字段(尤其是
TEXT
BLOB
)不会存进二级索引
EXPLAIN
Extra
列是否含
Using index
,有就是覆盖了;若出现
Using index condition
,说明用了 ICP(索引条件下推),也比全表扫描强

哪些场景下索引会失效或被忽略

即使建了索引,MySQL 也可能不走它。典型情况包括:统计信息过期、索引选择性太低(比如

gender
只有 'M'/'F')、查询返回大量行(优化器认为全表扫描反而更快)、或者存在更优的索引被误选。

运行
ANALYZE TABLE table_name
更新统计信息,尤其在大批量导入后
对低基数字段(如状态码、开关标志)建索引意义不大,除非配合高选择性条件(如
WHERE status = 'processing' AND updated_at > NOW() - INTERVAL 1 HOUR
FORCE INDEX
是临时手段,不能替代问题定位;真正要查的是为什么优化器没选对——通常得结合
SHOW INDEX
EXPLAIN
对比评估
EXPLAIN SELECT id, name FROM users WHERE deleted = 0 AND created_at > '2024-01-01';

索引设计不是一劳永逸的事。表结构变、数据分布变、查询模式变,原来高效的索引可能变成瓶颈。重点不是堆索引,而是理解每条慢查询背后的访问路径和数据特征。

相关推荐