<p>SELECT * 不是语法错误,但会隐式放大磁盘IO、网络传输、内存占用和执行计划不确定性——尤其在字段含 TEXT、BLOB 或表结构随时间膨胀时,性能退化会突然变得明显。</p>

直接说结论:SELECT * 不是语法错误,但会隐式放大磁盘IO、网络传输、内存占用和执行计划不确定性——尤其在字段含 TEXT、BLOB 或表结构随时间膨胀时,性能退化会突然变得明显。
为什么 SELECT * 会增加磁盘IO
数据库读取数据不是按“行”整体加载,而是按页(page)从磁盘载入缓冲池。每个页大小固定(如 InnoDB 默认 16KB),当语句写成 SELECT *,引擎必须把整行所有字段都读进来,哪怕你只用其中 1 个字段。
常见问题现象:
- 表里有
content字段类型为TEXT,单条记录实际占 50KB,但查询逻辑只需要id和title(共 128 字节)——SELECT *仍会把整个 50KB 拉进内存 - 垂直分表没做或滞后,大字段和高频查询字段混在同一张表,
SELECT *直接废掉覆盖索引机会
实操建议:
- 用
SHOW CREATE TABLE看清字段类型和长度,对 >1KB 的字段保持警觉 - 查监控时重点关注
Innodb_buffer_pool_read_requests与Innodb_buffer_pool_reads的比值,陡降可能就是*引起的物理读暴增
网络传输和应用层内存浪费更隐蔽
MySQL 协议把结果集序列化后通过 TCP 发给客户端。字段越多、越长,单次响应 payload 越大,不仅拖慢查询感知延迟,还会挤占连接带宽。
使用场景举例:
- Java 应用用
ResultSet.getObject()全量映射到实体类,即使只取id,JVM 仍要为所有字段分配对象引用和临时 byte[] - 微服务间用 JSON 透传
SELECT *结果,content字段导致单次响应超 2MB,触发网关限流或超时
参数差异提醒:
- MySQL 的
max_allowed_packet默认仅 4MB,大字段 + 星号易触发Packets larger than max_allowed_packet are not allowed - PostgreSQL 的
work_mem若被排序/聚合吃紧,*会更快耗尽内存,被迫落盘(spill to disk)
执行计划不稳定:优化器“猜不透”你要什么
数据库优化器依赖统计信息和谓词推导来选索引。SELECT * 让它无法判断是否能走覆盖索引,常导致本可避免的回表或全表扫描。
典型错误现象:
- 在
name字段建了索引,EXPLAIN显示type=ref,但Extra列写着Using where; Using index—— 这是覆盖索引;一旦改成SELECT *,Extra变成Using where; Using index; Using filesort或直接NULL,说明回表已发生 - 同一张表,
SELECT id, name FROM t WHERE status = 1走idx_status,而SELECT *却走了PRIMARY KEY,因为优化器认为回表成本低于索引扫描全部字段
实操建议:
- 永远用
EXPLAIN FORMAT=TREE(MySQL 8.0+)或EXPLAIN (ANALYZE)(PostgreSQL)验证实际执行路径 - 建组合索引时,把
SELECT中高频出现的字段放在索引末尾,例如常用SELECT user_id, created_at, status,就建INDEX idx_user_status (status, user_id, created_at)
最容易被忽略的一点:不是“现在慢才改”,而是“现在不改,将来某次加字段、升版本、涨流量时,它会第一个崩”。SELECT 后面写死列名,既是性能习惯,也是接口契约——谁动了表结构,谁就得同步核对所有 SELECT 语句。


















