当id 为主键时,select * from table where id =1 和 select id from table where id = 1 的效率是一样的吗?
在 MySQL(以最常用的 InnoDB 存储引擎为例)中,当 id 为主键时,这两条 SQL 的效率在绝大多数普通场景下差异极小,但在底层执行细节、资源开销以及特定表结构下,它们的效率是不一样的,SELECT id 往往会比 SELECT * 更高效。
具体的效率差异和底层原理可以从以下几个维度来分析:
1. 相同的底层执行路径(相同点)
- 索引寻路一致:在 InnoDB 中,主键
id是聚簇索引(Clustered Index)。无论执行哪一条语句,MySQL 优化器都会判定为const类型(即最快的常量等值查询)。 - B+ 树遍历一致:两者的执行都需要通过主键的 B+ 树索引,从根节点一路向下寻找到对应
id = 1的叶子节点。 - 基本 I/O 一致:如果要找的数据页(Page)不在内存的 Buffer Pool 中,两者都会触发一次相同的磁盘 I/O,将该行所在的数据页加载到内存中。
2. 不同的开销与处理细节(差异点)
一旦通过索引定位到了对应的行数据,两者的处理逻辑就产生了分歧,这导致了效率的差异:
① 溢出页(Overflow Pages)的磁盘 I/O 差异(关键影响)
- 如果表中有
TEXT、BLOB或非常长的VARCHAR字段:
在 InnoDB 中,当单行数据过大时,大字段的数据不会直接存放在聚簇索引的叶子节点内,而是被存放在单独的溢出页(Off-page)中,叶子节点只保留指向溢出页的指针。SELECT id:由于只需要id字段,MySQL 绝对不会去读取这些大字段的溢出页。SELECT *:因为要求返回所有字段,MySQL 必须顺着指针去额外读取这些溢出页,这会产生额外的磁盘或内存 I/O。在这种情况下,SELECT *的性能会明显下降。
② 内存与 CPU 解析开销
SELECT id:
由于WHERE id = 1已经限定了主键值,在 MySQL 的优化阶段,优化器甚至直接就能确定id的值,引擎层只需确认该行存在,不需要对行数据进行复杂的反序列化。SELECT *:
InnoDB 的行数据是以特定格式存储的。MySQL 必须在内存中把整行数据进行解析和拆解(Unpacking),将各个字段的值提取出来并拷贝到服务层的行缓冲区中。如果表的列数非常多,这个过程会消耗更多的 CPU 和内存。
③ 网络传输带宽
SELECT id:只传输一个整数(如 INT 占 4 字节,BIGINT 占 8 字节),网络报文极小。SELECT *:需要将该行的所有列数据通过网络发送给客户端。如果单行数据较大(如几 KB 到 几 MB),在高并发场景下,网络带宽和网卡 I/O 很容易成为瓶颈。
总结
- 在字段较少、没有大字段的简单表中,这两者的执行时间差异通常在微秒级别,在应用层几乎无法感知。
- 在包含大字段(TEXT/BLOB)、多列(数十甚至上百列)或高并发的场景下,
SELECT id的效率会明显优于SELECT *。
因此,从数据库最佳实践和性能优化的角度出发,即使是主键点查询,也依然强烈建议**“按需查询”,避免无脑使用 SELECT ***。
右滑查看面试常问