现在的ORM是否仍然与MS SQL上的SELECT *有关的性能/代码可维护性问题?

时间:2016-09-02 15:29:02

标签: sql sql-server orm database-performance sqlperformance

摘要:由于性能和可维护性问题,我在MS SQL中使用SELECT *看到了很多反对的建议。然而,这些帖子很多都很老 - 5到10年!很多帖子中的似乎,即使在他们的时间,性能问题实际上也可能非常小,而且可维护性问题(“哦不,如果有人更改列,并且您通过索引数组来获取数据!您的SELECT *会让您遇到麻烦!“),现代编码实践和ORM(如Dapper)似乎 - 至少在我的经验中 - 消除此类顾虑。

所以:SELECT *是否存在今天仍然存在的问题?

更大的背景:我已经开始在一个拥有大量旧代码(ASP脚本等)的地方工作,而且我一直在帮助实现大量的现代化,但是:我的大多数SQL经验实际上是来自MySQL和PHP框架和ORM - 这是我第一次使用MS SQL - 我知道两者之间存在细微差别。另外:我的同事比我年长一点,并且有一些担忧 - 对我来说 - 似乎“老了”。 (“可空的字段很慢!避免它们!”)但是又一次:在这个特定的领域,他们肯定比我更有经验。

出于这个原因,我还想问一下:现代ORM中的SELECT *是否安全,今天是不安全的,是否有最新的在线资源表明这样?

谢谢! :)

5 个答案:

答案 0 :(得分:14)

我不会在这个答案中触及可维护性,只有性能部分。

此上下文中的性能与ORM几乎没有关系。

服务器如何生成正在运行的查询,无论是手动编写还是由ORM生成,都无关紧要。

选择您不需要的列仍然是一个坏主意。

从性能的角度来看,查询是否如下所示并不重要:

SELECT * FROM Table

或明确列出所有列,例如:

SELECT Col1, Col2, Col3 FROM Table

如果您只需要Col1,请确保仅选择Col1。无论是手工编写查询还是微调ORM,都无关紧要。

为什么选择不必要的列是一个坏主意:

  • 从磁盘读取的额外字节

  • 通过网络传输的额外字节

  • 要在客户端上解析的额外字节

  • 但是,最重要的原因是优化工具可能无法生成好的计划。例如,如果存在包含所有请求列的覆盖索引,则服务器通常只读取此索引,但如果您请求更多列,则会执行额外查找或使用其他索引,或者只扫描整个表。最终影响可以从可忽略不计到秒与运行时间之间变化。数据库越大越复杂,您就越有可能看到明显的差异。

Myth: Select * is bad网站上有关于此主题Use the index, Luke的详细文章。

  

现在我们已经就选择的原因建立了共识   一切都不利于性能,你可能会问为什么它被列为一个   神话?这是因为很多人认为明星是坏事。   此外,他们认为他们没有犯下这种罪行,因为他们的罪行   无论如何,ORM按名称列出所有列。事实上,犯罪是选择   所有列都没有考虑它 - 大多数ORM都很容易提交   这个罪行代表他们的用户。

我会在这里添加你的评论的答案。

我不知道如何处理一个没有选择哪个字段的ORM。我个人会尽量不使用它。通常,ORM添加了leaks严重的抽象层。 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秒vs 100秒。最终用户肯定会注意到,即使比率(慢100倍)是相同的。实际上,随着数据的增长,这个比例会增加,因为各种缓存的用处越来越少。

答案 1 :(得分:6)

从SQL-Server-Performance-Point-of-view,你永远不应该使用select *,因为这意味着sqlserver从磁盘或ram读取完整的行。即使您需要所有字段,我建议不要select *,因为您不知道,谁将任何数据附加到您的应用程序不需要的表中。有关详细信息,请参阅@ sandip-patel的答案

从DBA角度来看:如果您准确提供所需的列名,dbadmin可以更好地分析和优化他的数据库。

从ORM-Point-Of-View更改列名,我建议不要使用select *。你想知道,如果表格改变了。如果基础表发生变化,如果没有出现错误,您希望如何保证应用程序运行并给出正确的结果?

个人意见:我真的不能在需要表现良好的应用程序中使用ORM ......

答案 2 :(得分:5)

这个问题已经过了一段时间了,似乎没有人能够找到Ben正在寻找的......

我认为这是,因为答案是"它取决于"。

只有不是一个的答案。

实施例

  • 正如我之前所指出的,如果数据库不是您的,并且可能经常更改,则无法保证性能,因为使用select *每行的数据量可能会爆炸
  • 如果你使用ITS OWN数据库编写一个应用程序,没有人改变你的数据库(希望如此),你需要你的列,所以选择*
  • 有什么不对
  • 如果您使用"主要属性构建某种延迟加载" beeing立即加载,其他人稍后加载(同一实体),你不能选择*因为你得到所有
  • 如果你使用select *,其他开发人员每次都会想到"他是否考虑过选择*"因为他们会尝试优化。所以你应该添加足够的评论......
  • 如果您在中间层构建3层应用程序构建大型缓存,并且性能是缓存完成的主题,您可以使用select *
  • 扩展3Tier:如果你有许多并发用户和/或非常大的数据,你应该考虑每一个字节,因为你必须扩大你的中间层,每个字节都浪费掉(正如有人在评论中指出的那样)前)
  • 如果你为3个用户和数千条记录构建一个小应用程序,预算可能没有时间来优化速度/ db-layout /某些东西
  • 与你的dba说话......他会建议你必须改变/优化/剥离/ ...。/ li>

我可以继续。没有一个答案。这取决于很多因素。

答案 3 :(得分:3)

通常最好明确选择列名。如果一个表收到一个额外的列,它将加载一个select *调用,其中不需要额外的列。

这可能有几个含义:

  • 更多网络流量

  • 更多I / O(必须从磁盘读取更多数据)

  • 可能更多的I / O(不能使用覆盖索引 - 执行表扫描以获取数据)

  • 可能更多的CPU(不能使用覆盖索引,因此数据需要排序)

<强>异常即可。 选择*正常的唯一位置是在Exists或Not Exists谓词子句之后的子查询中,如:

Select colA, colB
From table1 t1
Where Exists (Select * From Table2  Where column = t1.colA)

More Details -1

More Details -2

More Details -3

答案 4 :(得分:-1)

可维护性点。

如果您执行“从表中选择*”

然后我改变了表并添加了一列。

您的旧代码可能会崩溃,因为它现在有一个额外的列。

这会为将来的修订创建一个夜间母马,因为您必须识别select *的所有位置。

速度差异非常小,我不会担心。使用Varchar与Char之间存在速度差异,Char更快。但速度差异非常小,几乎不值得谈论。

选择*的最大问题是对表结构进行更改(添加)。

可维护性的噩梦。初级程序员的标志,以及糟糕的项目代码。话虽如此,我仍然使用select *但打算在我使用我的代码进行制作之前删除它。