select * 不是语法错误,但会强制mysql放弃覆盖索引、放大io与网络开销、让字段变更变成线上故障导火索——它把性能、安全和稳定性决策权交给了表结构和优化器的运气。

直接说结论:SELECT * 不是语法错误,但会强制 MySQL 放弃覆盖索引、放大 IO 与网络开销、让字段变更变成线上故障的导火索——它把本该由人控制的性能、安全和稳定性,全交给了表结构和查询优化器的运气。
SELECT * 为什么一定破坏覆盖索引
覆盖索引生效的前提,是 SELECT 的所有字段都落在同一个二级索引的叶子节点里。而 SELECT * 天然不满足这个条件:哪怕你建了 KEY idx_name_status (name, status),只要表里还有 id(主键)、created_at 或任意一个 TEXT/BLOB 字段,MySQL 就必须回表查聚簇索引。
-
EXPLAIN中看到Extra: Using where但没有Using index,基本就是被*拖累 -
TEXT、BLOB、超长VARCHAR(>728 字节)根本不会进任何索引,加进复合索引定义也会被 MySQL 静默忽略 - 即使只差一个字段没在索引中,优化器也放弃走覆盖,转为“先定位再回表”,IO 次数翻倍是常态
网络和内存开销远比想象中严重
MySQL 不压缩结果集,net_buffer_length 默认仅 16KB,但单行数据一旦含 icon_md5、profile JSON 或 log TEXT,很容易突破这个阈值,触发多次 socket send buffer 写入。
- DB 和应用跨机房时,大包 + TCP 往返 = 延迟陡增;同机部署也绕不开 loopback TCP 栈和内核拷贝
- ORM 如 MyBatis、Hibernate 会按行构造完整对象,哪怕业务只用
id和nick_name,其余字段照样加载进内存、参与 GC - 分页场景下,
LIMIT 10000,20配合SELECT *,MySQL 仍要构造并丢弃前 10000 行完整记录,CPU 和 buffer pool 压力肉眼可见
表结构一动,旧代码就 silently fail
字段顺序、数量、类型不再由 SQL 控制,而是由 ALTER TABLE 的执行时刻决定。这种不确定性在工程中极易引发隐性故障。
- 新增
backup_data LONGBLOB字段后,所有旧SELECT *查询可能突然变慢,监控只报“慢 SQL 数上升”,查不出根因 - MyBatis 的
resultMap若用下标绑定(如rs.getString(2)),字段顺序一变,name就可能映射成email或password_hash - 权限控制失效:DBA 给用户只开了
SELECT(name, phone)权限,SELECT *直接越权读出敏感字段 - 视图或分库中间件(如 ShardingSphere)解析元数据时,
*返回列序不可控,容易字段错位或解析失败
最常被忽略的一点:SELECT * 看似省事,实则把性能、安全、可维护性的决策权交给了数据库和时间——而这两者从不主动提醒你哪里错了。











