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
语句的数量也不会那么大:
【讨论】:
【参考方案2】:这很大程度上取决于您正在处理的数据量。 连接虽然返回更多字段,但运行速度明显快于选项 1 查询集(通常)。 根据我的个人经验,减速几乎总是在那个级别,即查询的实际运行,而不是通过任何管道传递的大量数据。
【讨论】:
以上是关于ORM 选择n+1个性能;加入或不加入的主要内容,如果未能解决你的问题,请参考以下文章