不推荐用 SELECT * 是因为它会放大 IO、网络、内存三重开销,并使 MySQL 优化器无法使用覆盖索引;具体表现为回表导致 IO 次数倍增、大字段触发溢出页读取、单行数据包膨胀拖慢网络、ORM 强制加载冗余字段占用内存,且易因表结构变更引发隐性故障。

不推荐用 SELECT * 不是因为语法错,而是它会直接放大 IO、网络、内存三重开销,且让 MySQL 优化器彻底放弃覆盖索引机会。
为什么 SELECT * 会让 IO 次数翻倍
InnoDB 引擎中,辅助索引(比如 INDEX(b))只存索引列 + 主键值,而聚集索引才存整行数据。当查询条件能走 b 索引,但语句写的是 SELECT *,MySQL 就必须:先查辅助索引定位主键,再回表查聚集索引拿全部字段——两次 B+ 树查找。
- 如果只需要
a和b,SELECT a,b FROM t WHERE b = ?可完全走辅助索引,数据全在内存页里,零磁盘 IO - 一旦改成
SELECT *,哪怕其他字段全是TINYINT,也要触发回表;若某字段是TEXT或超长VARCHAR(>728 字节),InnoDB 还会额外做溢出页读取,IO 次数可能变成三次 - buffer pool 命中率越低,这个差距越明显——线上常见 buffer pool 使用率 60%~80%,意味着大量
SELECT *实际在刷磁盘
SELECT * 怎么悄悄拖慢网络和客户端内存
字段越多、越宽,单行数据包体积越大,TCP 包数量和往返延迟就越高。即使 DB 和应用同机部署,依然走本地 loopback TCP 栈,无法绕过序列化/反序列化和内核缓冲区拷贝。
面向 CRM 系统的 MySQL 8 数据库模式设计。当用户需要为客户关系管理(CRM)系统设计、评审、优化或生成数据库模式时,应使用此项技能。
- 一个带
icon_md5 CHAR(32)、log TEXT、profile JSON的用户表,SELECT *平均单行 >12KB;显式指定id,nick_name,avatar_url后压到 200B 以内 - ORM 自动映射时,
SELECT *会强制把所有字段加载进对象属性,哪怕业务只用其中 2 个——Java 的ResultSet、Python 的cursor.fetchall()都按行分配内存,字段数 × 行数 × 平均宽度 = 实际堆内存占用 - 分页场景更危险:
LIMIT 10000,20配合SELECT *,MySQL 内部仍要构造并丢弃前 10000 行完整记录,CPU 和内存浪费严重
哪些情况看似“安全”,其实照样踩坑
有人觉得“表小、字段少、就我一个服务连”,SELECT * 没问题。但只要表结构一变,风险立刻暴露。
- 加了个
backup_data LONGBLOB字段?所有旧SELECT *查询瞬间变慢,监控可能只报“慢 SQL 数上升”,根本看不出根源 - 使用视图或中间件(如 MyCat、ShardingSphere),
SELECT *会导致元数据解析失败或字段错位——视图定义字段顺序和底层表不一致时,*返回列序不可控 - ALTER TABLE ADD COLUMN … FIRST / AFTER,会改变
SELECT *的字段物理顺序,某些强依赖列序的客户端代码(比如用rs.getObject(3)取第三个字段)直接出错 - 备份恢复后表结构微调(比如
ENUM值增删)、字符集变更(utf8mb4vsutf8),*查询可能因隐式转换导致索引失效,执行计划突变
最麻烦的不是性能掉点,而是问题不会立刻爆发:它藏在字段膨胀、索引策略退化、部署环境差异这些缓慢变化里。等监控告警时,往往已经连带引发连接池打满、GC 频繁、下游超时连锁反应。










