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

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

场景:

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

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

现在我有两个主要选择:

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

问题:

大多数关于 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 可以自己解决?

【问题讨论】:

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

标签: sql performance 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 查询集(通常)。 根据我的个人经验,减速几乎总是在那个级别,即查询的实际运行,而不是通过任何管道传递的大量数据。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-05-24
      • 1970-01-01
      • 2011-02-21
      • 2011-03-03
      • 2015-07-28
      • 1970-01-01
      • 2018-03-31
      • 1970-01-01
      相关资源
      最近更新 更多