生产环境禁止select *,因其在性能(元数据解析争抢锁)、稳定性(大字段触发额外磁盘i/o)、安全(字段变更破坏应用契约)三方面埋雷,并导致覆盖索引失效、回表成本激增。

生产环境业务查询中,SELECT * 不是“能用就行”,而是明确禁止——它会在性能、稳定性、安全三方面悄悄埋雷。
SELECT * 会让 MySQL 多做一次元数据解析
MySQL Server 层收到 SELECT * 后,必须先查数据字典(information_schema.COLUMNS),把星号展开成真实字段列表,再进入优化器。这个过程叫“列解析”,对单条语句影响小,但高并发下硬解析增多时,会争抢 metadata_lock,拖慢整体 QPS。而 SELECT id, name 直接跳过这步,解析更快、更确定。
大字段触发额外磁盘 I/O,尤其在 InnoDB 中
InnoDB 对超过 728 字节的字段(如 TEXT、BLOB、长 VARCHAR)会存到溢出页(off-page),SELECT * 强制读取这些页,哪怕你根本不用其中内容。结果就是:一次逻辑查询 → 多次随机磁盘寻道 → 延迟陡增。常见现象是慢查询日志里 Rows_examined 很小,但 Query_time 却很高,根源往往就在这里。
覆盖索引完全失效,回表成本翻倍
- 假设表
t_user有索引(status, name),你要查status = 'active'的name和id -
SELECT name, id FROM t_user WHERE status = 'active'→ 走索引覆盖,不回表 -
SELECT * FROM t_user WHERE status = 'active'→ 必须回聚集索引取所有列,哪怕只用其中两列
回表不是简单“多查一行”,而是按主键再次 B+ 树搜索,IO 次数线性增长。表越大、匹配行越多,差距越明显。
字段变更直接破坏应用层契约
ORM(如 MyBatis)或 JDBC ResultSet.getObject(2) 这类按位置取值的代码,依赖的是 SELECT 返回的**列顺序和数量**。SELECT * 把这种依赖藏得极深:
-
ALTER TABLE t ADD COLUMN phone VARCHAR(20) NOT NULL→ 所有未设默认值的SELECT *查询立刻报SQLException: Field 'phone' doesn't have a default value -
ALTER TABLE t MODIFY COLUMN email VARCHAR(255) FIRST→ 原本getObject(1)取的是id,现在变成email,类型错配直接抛ClassCastException
这类问题上线后才暴露,调试成本远高于写 SQL 时多敲几个字段名。
真正难的从来不是写出 SELECT id, name, created_at,而是想清楚:这一查到底要什么、谁消费、生命周期多长、下游是否缓存。漏掉任意一环,SELECT * 就不是省事,是透支技术债。











