select *不会直接使索引失效,但因回表成本高,优化器常选择全表扫描;explain显示type=all且key为空,是因顺序io优于大量随机io;高选择性条件仍可走索引,低选择性则倾向全表扫描。

SELECT * 不会直接让索引失效,但会让优化器大概率放弃走索引——因为回表成本太高,不如全表扫描来得快。
为什么EXPLAIN显示type=ALL却还有索引?
这是最常被误解的现象:明明WHERE条件字段有索引,EXPLAIN却显示type=ALL(全表扫描),key为空。这不是索引坏了,而是优化器权衡后选了顺序IO而非大量随机IO。
- 当
SELECT *配合高选择性WHERE(比如id = 123)时,通常仍能走索引——因为只查1行,回表开销小 - 但WHERE过滤后结果集占表总量超20%~30%,比如
status = 'active'匹配8万行,优化器会判定:用索引扫出8万个主键再逐个回表,不如直接顺序读整张表 -
EXTRA列若出现Using where; Using filesort或Using temporary,说明不仅没走覆盖索引,还触发了额外计算步骤
如何用EXPLAIN快速判断是否真需要改写?
在任意查询前加EXPLAIN,重点关注三列:type、key、Extra。不是所有SELECT *都该改,但以下信号必须处理:
-
type是ALL或index,且rows远大于实际返回行数 → 很可能没走对索引,或索引未覆盖 -
Extra含Using filesort→ORDER BY字段没索引,或索引顺序不匹配 -
Extra含Using temporary→ 比如GROUP BY+SELECT *,临时表无法避免回表 -
key非空但rows极大(如百万级),而业务只用其中2~3个字段 → 回表成为瓶颈,应建覆盖索引
哪些场景下SELECT *其实暂时可忍?
不是所有SELECT *都得立刻砍掉。以下情况可暂缓重构,但需监控:
- 单行主键查询:
SELECT * FROM users WHERE id = ?—— 回表只有1次,影响微乎其微 - 小表(TEXT/
BLOB)—— 内存和I/O压力低 - 调试/运维临时查表结构:
SELECT * FROM config LIMIT 5—— 明确带LIMIT且非线上高频调用 - ORM自动生成的初始化语句(如Rails的
User.first)—— 若后续立即访问全部字段,改列名反而增加维护成本
真正要动手改写的临界点在哪?
当出现以下任一组合,就必须改:SELECT * + 大表 + 非主键WHERE + 有TEXT/BLOB字段 + 并发QPS > 50。这时性能衰减不是线性,而是指数级。
- 第一步:用
SELECT id, name, status代替SELECT *,观察EXPLAIN中rows是否显著下降 - 第二步:如果
WHERE和SELECT字段能共同落在一个联合索引里(如(status, name, id)),加索引后Extra会变成Using index,彻底消除回表 - 第三步:检查应用层是否真的用了所有字段——很多
SELECT *查出来后只取id和name,其余字段纯属浪费带宽和内存
最隐蔽的坑是:开发阶段看不出问题,一上生产,慢查询日志里全是SELECT *开头的语句,而DBA只看到“没走索引”,却没意识到罪魁祸首是星号本身带来的回表放大效应。











