SQL必知必会 | 到底为什么不建议使用SELECT * ?

238 阅读4分钟

困惑由来

从 Customers 表中检索所有的 ID
这是《SQL必知必会》里面的第一道题目,也是最简单的检索
【描述】

现有表Customers如下:

cust_id
A
B
C

【问题】

编写 SQL 语句,从 Customers 表中检索所有的cust_id

【示例答案】

返回cust_id列的内容

cust_id
A
B
C

【示例1】

输入:

DROP TABLE IF EXISTS `Customers`;

CREATE TABLE IF NOT EXISTS `Customers`(
    cust_id VARCHAR(255) DEFAULT NULL
);

INSERT `Customers` VALUES ('A'),('B'),('C');

输出:

A
B
C

相信大部分人觉得这实在太简单了,直接select * from Customer不就行了吗
但是我要说的是尽量不要使用select *,我刚开始也是直接select *,但是看到了下面这个题解
于是就用select cuct_id from Customer重新写了一遍,写明了要查询的字段
我也由此感到迷惑,select *的性能损耗真有这么大吗?
真的只是因为性能而不建议使用select 吗? ef673a7d4fe0b323becf35a000b7141.png

原因一、性能差

在《阿里巴巴开发手册》的ORM映射章节中有一条【强制】的规范:

4 - 1. 【强制】在表查询中,一律不要使用 * 作为查询的字段列表,需要哪些字段必须明确写明。

说明:

  • 增加查询分析器解析成本。
  • 增减字段容易与 resultMap 配置不一致。
  • 无用字段增加网络 消耗,尤其是 text 类型的字段。

开发手册中比较概括的提到了几点原因,让我们深入一些看看:

1、不需要的列会增加数据传输时间和网络开销

  • 用“SELECT * ”数据库需要解析更多的对象、字段、权限、属性等相关内容,在 SQL 语句复杂,硬解析较多的情况下,会对数据库造成沉重的负担。
  • 增大网络开销;* 有时会误带上如log、IconMD5之类的无用且大文本字段,数据传输size会几何增涨。如果DB和应用程序不在同一台机器,这种开销非常明显
  • 即使 mysql 服务器和客户端是在同一台机器上,使用的协议还是 tcp,通信也是需要额外的时间。

2、对于无用的大字段,如 varchar、blob、text,会增加 io 操作

准确来说,长度超过 728 字节的时候,会先把超出的数据序列化到另外一个地方,因此读取这条记录会增加一次 io 操作。(MySQL InnoDB)

3、失去MySQL优化器“覆盖索引”策略优化的可能性

SELECT * 杜绝了覆盖索引的可能性,而基于MySQL优化器的“覆盖索引”策略又是速度极快,效率极高,业界极为推荐的查询优化方式。

原因二、可维护性和稳定性差

一个项目只要维护时间足够长,后期加字段是极大概率事件。

换句话说,select * 在你当时调试程序时或许返回5个字段,而未来返回的可能是十个二十个。

如果连查出来的结果都不确定,你处理结果的程序逻辑该怎么写?你该给查询结果留多大的内存空间或者存储空间?如果加的字段数据量极大,你又如何预料到未来出现莫名的性能下降?

除非你这个查询的处理行为就是不管三七二十一给查询结果建一张表格来显示,或者单纯的打印输出(这只会存在于调试输出日志的情况),否则,select * 的返回值不确定,程序将不稳定,容易在未来出bug爆雷。 我个人的观点一开始就很明确:

一个项目建设之初不能预见未来数据库会演变成什么样。SELECT * 有可能扫描不需要的列,而SELECT 列名只扫描声明的列。也许写 * 大多数时间都没问题,但是仍有情况会出现问题,而这些问题只需要吧 * 给换成列名就能杜绝。

参考资料

为啥《阿里开发手册》一律禁止使用SELECT * ? - 蜘蛛侠不会飞 - 博客园 (cnblogs.com)
到底为什么不建议使用SELECT * ?