摘要:由于性能和可维护性的问题,我看到了很多反对在MS SQL中使用SELECT*的建议。然而,这些帖子中的许多都很老了——5到10年!从这些帖子中,似乎性能问题实际上可能很小,即使在他们的时代也是如此,至于可维护性问题("哦,不,如果有人更改了列,而你是通过索引数组来获取数据的!你的SELECT*会给你带来麻烦的!"),现代编码实践和ORM(如Dapper)似乎(至少根据我的经验)可以消除这些问题。
那么:SELECT*是否存在今天仍然相关的问题?
更大的背景:我开始在一个有很多旧MS代码(ASP脚本等)的地方工作,我一直在帮助实现其中的许多现代化:我的大部分SQL经验实际上来自MySQL、PHP框架和ORM-这是我第一次使用MS SQL-我知道两者之间有细微的区别。此外:我的同事比我大一点,他们有些担心——对我来说——似乎"老了"。("可为null的字段很慢!避免它们!")但是:在这个特定的字段中,他们肯定比我有更多的经验。
出于这个原因,我还想问:使用现代ORM的SELECT*在今天是否安全和理智,最近的在线资源是否表明了这一点?
谢谢!:)
在这个答案中我不会涉及可维护性,只涉及性能部分。
这种情况下的性能与ORM几乎没有关系。
对于服务器来说,它运行的查询是如何生成的并不重要,无论它是手工编写的还是由ORM生成的。
选择不需要的列仍然是个坏主意。
从性能的角度来看,查询是否像并不重要
SELECT * FROM Table
或者所有列都明确列在那里,比如:
SELECT Col1, Col2, Col3 FROM Table
如果只需要Col1
,请确保只选择Col1
。无论是通过手工编写查询还是通过微调ORM来实现,这都无关紧要。
为什么选择不必要的列是个坏主意:
要从磁盘读取的额外字节
通过网络传输的额外字节
要在客户端上解析的额外字节
但是,最重要的原因是优化器可能无法生成一个好的计划。例如,如果有一个包含所有请求列的覆盖索引,服务器通常只读取该索引,但如果您请求更多列,它会进行额外查找或使用其他索引,或者只扫描整个表。最终的影响可能在几秒钟到几小时之间变化。数据库越大、越复杂,就越有可能看到明显的差异。
有一篇关于这个主题的详细文章,Myth:Select*在使用索引,Luke网站上很糟糕。
现在我们已经对为什么选择一切都对性能不利,你可能会问为什么它被列为神话这是因为很多人认为明星是坏事。此外,他们认为自己没有犯下这一罪行,因为他们ORM仍然按名称列出所有列。事实上,犯罪是选择所有列都不考虑它——而且大多数ORM都很容易提交这一罪行代表了他们的用户。
我会在这里为您的评论添加答案。
我不知道如何处理一个ORM,它不给我选择哪些字段的选项。我个人尽量不使用它。一般来说,ORM添加了一层严重泄漏的抽象。https://en.wikipedia.org/wiki/Leaky_abstraction
这意味着您仍然需要知道如何编写SQL代码以及DBMS如何运行这些代码,但也需要知道ORM是如何工作和生成这些代码的。如果你选择不知道ORM背后发生了什么,当你的系统变得微不足道时,你会遇到无法解释的性能问题。
你说,在你之前的工作中,你使用ORM来处理一个大型系统,没有出现任何问题。它对你有效。好的不过,我有一种感觉,你的数据库并不是很大(你有几十亿行吗?),系统的性质允许将性能问题隐藏在缓存后面(这并不总是可能的)。系统的增长可能永远不会超过硬件容量。如果您的数据适合缓存,通常在任何情况下都会相当快。只有当你跨过某个门槛时,它才开始变得重要。之后,突然一切都变得缓慢,很难修复。
对于业务/项目经理来说,忽视可能永远不会发生的未来问题是很常见的。商业总是有更紧迫的问题需要处理。如果业务/系统在性能成为问题时增长足够,它要么已经积累了足够的资源来重构整个系统,要么将继续以越来越低的效率工作,或者如果系统恰好对业务非常关键,那么就失败,给另一家公司一个超越它的机会。
回答您的问题"是否在性能非常重要的应用程序中使用ORM"。当然你可以使用ORM。但是,你可能会发现它比不使用它更困难。考虑到ORM和性能,你必须手动检查ORM生成的SQL代码,并从性能的角度确保它是一个好代码。因此,您仍然需要了解SQL和您使用得很好的特定DBMS,并且您需要非常了解您的ORM,以确保它生成您想要的代码。为什么不直接编写您想要的代码呢?
您可能认为ORM与原始SQL的这种情况有点类似于高度优化的C++编译器与手动在汇编程序中编写代码的情况。事实并非如此。在大多数情况下,现代C++编译器确实会生成比在汇编程序中手动编写的代码更好的代码。但是,编译器非常了解处理器,优化任务的性质比数据库中的任务简单得多。ORM不知道你的数据量,它对你的数据分布一无所知。
top-n-per-group
的简单经典示例可以通过两种方式完成,最佳方法取决于只有开发人员知道的数据分布。如果性能很重要,那么即使您手工编写SQL代码,您也必须了解DBMS是如何工作的,并解释此SQL代码,并以DBMS以最佳方式访问数据的方式布置代码。SQL本身是一个高级抽象,可能需要进行微调才能获得最佳性能(例如,SQL Server中有几十个查询提示)。DBMS有一些统计信息,它的优化器试图使用它,但通常还不够。
现在,在此之上添加了另一层ORM抽象。
说了这么多,"性能"是一个模糊的术语。所有这些关注点在达到一定阈值后变得重要。由于现代硬件非常好,这个门槛被推得很高,以至于很多项目都可以忽略所有这些问题。
示例。对具有百万行的表的最佳查询将在10毫秒内返回。非最优查询将在1秒内返回。速度慢100倍。最终用户会注意到吗?也许吧,但可能并不重要。将表增加到十亿行,或者由一个用户增加到1000个并发用户。1秒对100秒。最终用户肯定会注意到,尽管比率(慢100倍)是相同的。在实践中,这个比率会随着数据的增长而增加,因为各种缓存的用处会越来越小。
从SQL Server性能的角度来看,您永远不应该使用select *
,因为这意味着SQL Server要从磁盘或ram读取完整的行。即使您需要所有字段,我也建议不要执行select *
,因为您不知道是谁在向表中添加您的应用程序不需要的任何数据。有关详细信息,请参阅@sandip patel 的回答
从DBA的角度来看:如果您给出了所需的列名,dbadmin可以更好地分析和优化他的数据库。
从更改列名的ORM角度来看,我建议不要使用select *
。你想知道,如果表改变了。如果在基础表发生更改时没有出现错误,您希望如何保证应用程序的运行并给出正确的结果??
个人意见:我真的不与ORM在应用程序需要表现良好。。。
这个问题已经解决了一段时间,似乎没有人能找到Ben在找什么。。。
我认为是的,因为答案是"视情况而定"。
这只是一个答案。
示例
- 正如我之前指出的,如果数据库不是您的,并且可能经常被更改,则无法保证性能,因为使用select*,每行的数据量可能会爆炸式增长
- 如果您使用ITS OWN数据库编写应用程序,没有人会更改您的数据库(希望如此),并且您需要您的列,那么select有什么错呢*
- 如果您构建了某种延迟加载,其中"主要属性"立即加载,其他属性稍后加载(同一实体),则无法使用select*,因为您将获得所有属性
- 如果你使用select*,其他开发人员每次都会考虑"他考虑过select*吗",因为他们会尝试优化。所以你应该添加足够的评论
- 如果您构建三层应用程序,在中间层构建大型缓存,并且性能是由缓存完成的主题,则可以使用选择*
- 扩展3Tier:如果你有很多并发用户和/或真正的大数据,你应该考虑每一个字节,因为你必须扩大你的中间层,每一个比特都会被浪费(正如之前有人在评论中指出的那样)
- 如果你为3个用户和数千条记录构建一个小型应用程序,预算可能没有时间优化速度/数据库布局/其他
- 与您的dba交谈。。。他会建议你哪些语句必须更改/优化/精简/
我可以继续说下去。只是没有一个答案。这取决于许多因素。
通常最好显式选择列名。如果一个表接收到一个额外的列,那么它将用一个select*调用加载,在这个调用中不需要额外的列。
这可能有几个含义:
-
更多网络流量
-
更多I/O(从磁盘读取更多数据)
-
可能会有更多的I/O(不能使用覆盖索引-执行表扫描以获取数据)
-
可能会有更多的CPU(不能使用覆盖索引,因此数据需要排序)
异常。Select*为OK的唯一位置是在Exists或Not Exists谓词子句之后的子查询中,如:
Select colA, colB
From table1 t1
Where Exists (Select * From Table2 Where column = t1.colA)
更多详细信息-1
更多详细信息-2
更多详细信息-3
可维护性点。
如果您执行"从表中选择*">
然后我修改表并添加一列。
你的旧代码可能会崩溃,因为它现在有一个额外的列。
这为未来的修订创造了一个噩梦,因为你必须确定选择*的所有位置。
速度差是如此之小,我不会担心。使用瓦查尔和查尔有速度差,查尔更快。但速度差太小了,根本不值得谈论。
Select*最大的问题是对表结构的更改(添加)。
可维护性噩梦。初级程序员的标志,以及糟糕的项目代码。话虽如此,我仍然使用select*,但打算在代码投入生产之前将其删除。