ORM 选择n+1个性能;加入或不加入

Posted

技术标签:

【中文标题】ORM 选择n+1个性能;加入或不加入【英文标题】:ORM Select n + 1 performance; join or no join 【发布时间】:2009-09-02 17:39:24 【问题描述】:

有类似的问题,但我认为没有人问过这个特定的问题。

场景:

客户 - 订单(其中订单有客户 ID) - OrderPart - 零件

我想要一个返回客户及其所有订单和每个订单及其零件的查询。

现在我有两个主要选择:

    使用嵌套循环(生成单独的查询) 使用数据加载选项(生成单个查询连接)

问题:

大多数关于 ORM 的建议和示例都建议使用选项 2,我明白为什么。但是,选项 2 可能会发回大量重复数据,例如:

选项 1 结果(3 个查询):

ID 名称 国家
1 个客户 1 个英国

身份证名称
1 订单1
2 订单2

身份证名称
1 第 1 部分
2 第 2 部分
3 第 3 部分

选项 2 结果(1 个查询):

ID 名称 国家 ID 名称 ID 名称
1 客户 1 英国 1 订单 1 1 第 1 部分
1 客户 1 英国 1 订单 1 2 第 2 部分
1 客户 1 英国 1 订单 1 3 第 3 部分
1 客户 1 英国 2 订单 2 1 第 1 部分
1 客户 1 英国 2 订单 2 2 第 2 部分

选项 1 发回 13 个字段和 3 个查询。选项 2 在 1 个查询中发回 42 个字段。现在想象一下 Customer 表有 30 个字段,而 Orders 有更复杂的子连接,数据重复很快就会变得巨大。

以下因素对整体性能有何影响:

建立数据库连接的开销 发送数据所花费的时间(如果在不同的服务器上,可能会通过网络) 带宽

选项 2 始终是最佳选择,选项 1 是最佳选择还是取决于具体情况?如果视情况而定,您应该使用什么标准来确定?是否有足够聪明的 ORM 可以自己解决?

【问题讨论】:

此查询/方案多久运行一次?数据库回答这两个选项有多难(基于数据库负载和数据量)?任何答案(对于现实世界)都需要考虑这些以及更多方面。 【参考方案1】:

建立数据库连接的开销

如果它们在同一个子网中,它们通常是很少的。如果不是,那么这仍然不是一个巨大的开销,并且可以通过缓存来克服,大多数 ORM 都有(NHibernate 有一级和二级缓存)。

发送数据所花费的时间(如果在不同的服务器上,可能会通过网络)

对于SELECT N+1,这显然会更长,因为它每次都必须发送选择语句,最长可能长达 1k。它还必须从池中获取新连接。在 2002 年至 2003 年左右,Chatty 与 chunky 曾经是一个争论,但现在除非这是一个非常大的应用程序,否则它实际上并没有太大的区别,在这种情况下,您可能需要一位更有经验(或报酬更高)的专家发表他的观点- 即顾问。

不过,我更喜欢联接,因为数据库将在 10 年或更长时间的开发过程中针对这种用途进行优化。如果性能真的很慢,视图可以解决这个问题,或者存储过程。

顺便说一句,SELECT N+1 可能是人们第一次开始使用 NHibernate 时遇到的最常见的性能问题(包括我),并且实际上需要调整才能解决。这是因为 NHibernate 之于 ORM 就像 C++ 之于语言。

带宽

每个Customer 的额外SELECT 语句最终将建立到许多Customer 对象* Orders。所以对于一个大型系统来说,这可能很明显——但正如我所提到的,ORM 通常有缓存机制来解决这个问题。考虑到SELECT 语句的数量也不会那么大:

您大部分时间都与 SQL 服务器在同一个网络中 增加的字节数会增加大约 0.5-50k 的额外带宽?想想在大多数服务器上这有多快。

【讨论】:

【参考方案2】:

这很大程度上取决于您正在处理的数据量。 连接虽然返回更多字段,但运行速度明显快于选项 1 查询集(通常)。 根据我的个人经验,减速几乎总是在那个级别,即查询的实际运行,而不是通过任何管道传递的大量数据。

【讨论】:

以上是关于ORM 选择n+1个性能;加入或不加入的主要内容,如果未能解决你的问题,请参考以下文章

.net core 3.1 加入ORM框架(Dapper)

加入前与加入后选择列

为啥加入从视图中选择前 N 比加入视图要快得多?

加入两个选择语句

在不选择所需列的情况下加入表或加入表中列的子选择是不是更有效

微信小程序插入个性化地图