|
|
1
851
假设你收集了
现在,假设您需要遍历所有的汽车,并为每个汽车打印出一个车轮列表。原始的O/R实现将执行以下操作:
然后
对于每一个
换句话说,您有一个选择用于汽车,然后有n个附加选择,其中n是汽车总数。 或者,可以获取所有车轮并在内存中执行查找:
这会将到数据库的往返次数从n+1减少到2。 大多数ORM工具都为您提供了几种防止N+1选择的方法。 参考文献: Java Persistence with Hibernate ,第13章。 |
|
|
2
100
这将为您提供一个结果集,其中表2中的子行通过返回表2中每个子行的表1结果而导致重复。O/R映射器应该基于唯一的键字段区分Table1实例,然后使用所有Table2列填充子实例。
n+1是第一个查询填充主对象,第二个查询填充返回的每个唯一主对象的所有子对象。 考虑:
和结构相似的桌子。地址“22 Valley St”的单一查询可返回:
O/RM应该用id=1填充一个home实例,address=“22 valley st”,然后只需一个查询就可以用dave、john和mike的people实例填充居民数组。 对于上面使用的相同地址的N+1查询将导致:
有一个单独的查询
并产生一个独立的数据集
最后的结果与上面的单查询相同。 单次选择的好处是,您可以提前获得所有数据,这可能是您最终想要的。n+1的优点是减少了查询复杂性,并且您可以使用延迟加载,其中子结果集仅在第一次请求时加载。 |
|
|
3
60
与产品有一对多关系的供应商。一个供应商有(供应)许多产品。
因素:
提取模式为选择提取(默认)
结果:
这是N+1选择问题! |
|
|
4
36
我不能直接对其他答案发表评论,因为我没有足够的声誉。但值得注意的是,这个问题本质上只会出现,因为在历史上,很多DBMS在处理连接时都相当糟糕(MySQL是一个特别值得注意的例子)。因此,n+1通常比一个连接要快得多。还有一些方法可以改进n+1,但是仍然不需要连接,这就是最初的问题所涉及的。 然而,MySQL现在比以前在连接方面要好得多。当我第一次学习mysql的时候,我经常使用join。然后我发现它们有多慢,于是在代码中改为n+1。但是,最近,我又回到了Join,因为与我刚开始使用MySQL时相比,MySQL现在在处理它们方面有了很大的进步。 现在,在性能方面,对一组索引正确的表进行简单的联接很少是一个问题。如果它确实会影响性能,那么使用索引提示通常可以解决问题。 这里由MySQL开发团队讨论: http://jorgenloland.blogspot.co.uk/2013/02/dbt-3-q3-6-x-performance-in-mysql-5610.html 所以总结是:如果您过去一直在避免连接,因为MySQL对它们的糟糕性能,那么在最新版本上再试一次。你可能会感到惊喜。 |
|
|
5
26
因为这个问题,我们离开了在Django的ORM。基本上,如果你试着去做
ORM会很高兴地返回所有人(通常作为Person对象的实例),但随后它需要为每个人查询car表。 我称之为一种简单而有效的方法。” 扇形折叠 “,这避免了从关系数据库查询结果应映射回组成查询的原始表的荒谬想法。 步骤1:宽选择
这将返回类似
第二步:客观化 将结果吸收到一个通用对象创建者中,并在第三个项后使用要拆分的参数。这意味着“琼斯”物体不会被制造一次以上。 步骤3:渲染
见 this web page 为了实现 扇形折叠 用于Python。 |
|
|
6
17
假设你有公司和员工。公司有很多员工(即员工有一个外勤公司ID)。
在某些O/R配置中,当您有一个映射的Company对象并访问其Employee对象时,O/R工具将为每个员工进行一次选择,如果您只是在纯SQL中进行操作,则可以
这就是EJB实体bean的初始版本的工作原理。我相信像Hibernate这样的事情已经解决了,但我不太确定。大多数工具通常包括有关其映射策略的信息。 |
|
|
7
14
这里有一个很好的问题描述- http://www.realsolve.co.uk/site/tech/hib-tip-pitfall.php?name=why-lazy 现在您了解了这个问题,通常可以通过在查询中执行join fetch来避免这个问题。这基本上强制获取延迟加载对象,以便在一个查询中检索数据,而不是N+1查询。希望这有帮助。 |
|
|
8
13
在我看来,这篇文章写在 Hibernate Pitfall: Why Relationships Should Be Lazy 正相反的是真正的N+1问题是。 如果您需要正确的解释,请参阅 Hibernate - Chapter 19: Improving Performance - Fetching Strategies
|
|
|
9
13
查看Ayend Post的主题: Combating the Select N + 1 Problem In NHibernate 基本上,当使用诸如nhibernate或entityframework这样的ORM时,如果您有一对多(master-detail)关系,并且想要列出每个主记录的所有详细信息,则必须对数据库进行n+1查询调用,“n”是主记录数:1个查询获取所有主记录,n个查询每个主记录,以获取每个主记录的所有详细信息。 更多的数据库查询调用——以及更多的延迟时间——&降低应用程序/数据库性能。 然而,ORM可以选择避免这个问题,主要是使用“连接”。 |
|
|
10
10
提供的链接有一个非常简单的n+1问题的例子。如果你把它应用于冬眠,基本上就是在说同样的事情。当查询对象时,将加载实体,但任何关联(除非另有配置)都将被延迟加载。因此,一个对根对象的查询和另一个为每个根对象加载关联的查询。返回的100个对象表示一个初始查询,然后再返回100个附加查询,以获取每个对象的关联,n+1。 |
|
11
10
当您忘记获取一个关联,然后需要访问它时,就会发生N+1查询问题:
它生成以下SQL语句:
首先,Hibernate执行jpql查询和
然后,为每个
因为
首先,你需要 proper SQL logging and monitoring 这样你就能发现这个问题。 第二,这种问题最好被集成测试捕获。你可以使用 automatic JUnit assert to validate the expected count of generated SQL statements . 这个 db-unit project 已经提供了这个功能,而且它是开源的。 当您确定了N+1查询问题时, you need to use a JOIN FETCH so that child associations are fetched in one query, instead of N . 如果需要获取多个子关联,最好在初始查询中获取一个集合,在第二个集合中获取一个辅助SQL查询。 |
|
|
12
9
发出一个返回100个结果的查询要比发出100个每个返回1个结果的查询快得多。 |
|
|
13
7
一个百万富翁有N辆车。你想要所有(4)个轮子。 一(1)个查询加载所有汽车,但对于每(n)辆汽车,将为加载车轮提交单独的查询。 成本: 假设索引适合RAM。 1+n查询解析和规划+索引搜索和1+n+(n*4)板访问加载有效载荷。 假设索引不适合RAM。 最坏情况下,加载指数的1+N板通道的额外成本。 总结 瓶颈是板接入(大约每秒70次HDD随机接入) 一个热切的连接选择也将访问板1+n+(n*4)倍的有效载荷。 所以如果索引适合RAM——没问题,它足够快,因为只涉及RAM操作。 |
|
|
14
7
N+1选择问题是一个难题,在单元测试中检测这种情况是有意义的。 我开发了一个小库,用于验证给定测试方法或任意代码块执行的查询数。- JDBC Sniffer 只需向测试类中添加一个特殊的JUnit规则,并在测试方法上放置带有预期查询数的注释:
|
|
|
15
5
正如其他人所说,更优雅的问题是,要么您拥有一个任意列的笛卡尔积,要么您正在进行n+1选择。可能是巨大的结果集,也可能是与数据库聊天。
我很惊讶没有提到这个问题,但我是如何解决这个问题的…
我做了一个半临时的身份证表
.
I also do this when you have the
这并不适用于所有情况(可能不是大多数情况),但如果您有许多子对象,例如笛卡尔积将失控(即许多
首先将父对象ID作为批插入到ID表中。 这个批处理ID是我们在应用程序中生成并保存的。
现在为每个
然后定期清理IDS表。 如果用户选择100个左右不同的项目进行某种批量处理,这也特别有效。将100个不同的ID放入临时表中。 现在,您正在执行的查询的数量是按OneTomany列的数量计算的。 |
|
|
16
1
以Matt Solnit为例,假设您将汽车和车轮之间的关联定义为“懒惰”,并且需要一些车轮字段。这意味着在第一次选择之后,Hibernate将对每辆车执行“从车轮中选择*,其中car id=:id”。 这使得第一个选择和更多的1选择每个N车,这就是为什么它被称为N+1问题。 要避免这种情况,请使关联获取变得更加迫切,以便Hibernate使用联接加载数据。 但是请注意,如果很多时候您不访问相关联的轮子,最好让它保持懒惰,或者根据条件更改fetch类型。 |