首页 / 知识
ORM(对象关系映射)中的“N + 1选择问题”是什么?
2023-04-17 10:16:00

What is the “N+1 selects problem” in ORM (Object-Relational Mapping)?"N + 1选择问题"通常被称为对象关系映射(ORM)讨论中的一个问题,我理解它必须为对象中看起来很简单的事情做出大量的数据库查询。 世界。 有没有人对这个问题有更详细的解释?
假设您有一组 现在,假设您需要遍历所有车辆,并为每个车辆打印出车轮列表。天真的O / R实现将执行以下操作:
然后为每个
换句话说,您有一个选择汽车,然后N个额外选择,其中N是汽车总数。 或者,可以获得所有轮子并在内存中执行查找:
这减少了从N + 1到2的数据库往返次数。 参考:Java Persistence with Hibernate,第13章。
这会得到一个结果集,其中table2中的子行通过返回table2中每个子行的table1结果而导致重复。 O / R映射器应根据唯一键字段区分table1实例,然后使用所有table2列填充子实例。
N + 1是第一个查询填充主对象的位置,第二个查询填充返回的每个唯一主对象的所有子对象。 考虑:
和具有类似结构的表格。地址"22 Valley St"的单个查询可能会返回:
O / RM应该填充ID = 1,Address ="22 Valley St"的Home实例,然后用Dave,John和Mike的People实例填充Inhabitants数组,只需一个查询。 对上面使用的相同地址的N + 1查询将导致:
用一个单独的查询
并产生一个单独的数据集
并且最终结果与单个查询的上述相同。 单一选择的优点是您可以预先获得所有数据,这可能是您最终想要的。 N + 1的优点是减少了查询复杂性,并且您可以使用延迟加载,其中子结果集仅在第一次请求时加载。 与产品具有一对多关系的供应商。一个供应商拥有(供应)许多产品。
影响因素:
获取模式为Select Fetch(默认)
结果:
这是N + 1选择问题! 我不能直接评论其他答案,因为我没有足够的声誉。但值得注意的是,问题基本上只会产生,因为从历史上看,许多dbms在处理连接方面都很差(MySQL是一个特别值得注意的例子)。所以n + 1通常比连接快得多。然后有一些方法可以改进n + 1,但仍然不需要连接,这是原始问题所涉及的。 但是,MySQL现在比以前的连接要好得多。当我第一次学习MySQL时,我使用了很多连接。然后我发现它们有多慢,并在代码中切换到n + 1。但是,最近,我一直在回到加入,因为MySQL在处理它们时比我刚开始使用它时要好得多。 目前,在性能方面,对正确索引的表集合的简单连接很少成为问题。如果它确实给性能带来了影响,那么使用索引提示通常会解决它们。 这是由MySQL开发团队之一讨论的: http://jorgenloland.blogspot.co.uk/2013/02/dbt-3-q3-6-x-performance-in-mysql-5610.html 所以摘要是:如果你因为MySQL的糟糕表现而过去一直在避免加入,那么再试一次最新版本。你可能会感到惊喜。 由于这个问题,我们离开了Django的ORM。基本上,如果你尝试做
ORM将很乐意返回所有人(通常作为Person对象的实例),但随后它将需要查询每个Person的car表。 一种简单而有效的方法是我称之为"粉丝折叠",它避免了一种荒谬的想法,即来自关系数据库的查询结果应该映射回构成查询的原始表。 第1步:广泛选择
这将返回类似的东西
第2步:客观化 将结果吸收到通用对象创建器中,并在第三个项目后分割参数。这意味着"jones"对象不会多次出现。 第3步:渲染
有关python的fanfolding的实现,请参阅此网页。 假设您有公司和员工。公司有许多员工(即员工有一个字段COMPANY_ID)。
在一些O / R配置中,当你有一个映射的Company对象并且去访问它的Employee对象时,O / R工具会为每个员工做一个选择,如果你只是在直接SQL中做事,你可以 这就是EJB Entity Beans的初始版本的工作方式。我相信像Hibernate这样的东西已经废除了这个,但我不太大多数工具通常都包含有关其映射策略的信息。 这是对问题的一个很好的描述 - https://web.archive.org/web/20160310145416/http://www.realsolve.co.uk/site/tech/hib-tip-pitfall.php?name=why-懒 现在您已经了解了这个问题,通常可以通过在查询中进行连接提取来避免它。这基本上强制获取延迟加载的对象,因此在一个查询中检索数据而不是n + 1个查询。希望这可以帮助。 查看关于主题的Ayende帖子:在NHibernate中解决选择N + 1问题 基本上,当使用像NHibernate或EntityFramework这样的ORM时,如果你有一对多(主 - 细节)关系,并希望列出每个主记录的所有细节,你必须对N + 1查询调用数据库,"N"是主记录的数量:1个查询获取所有主记录,N个查询(每个主记录一个)获取每个主记录的所有详细信息。 更多数据库查询调用 - >更多延迟时间 - >降低应用程序/数据库性能。 但是,ORM可以选择避免这个问题,主要是使用"连接"。 当您忘记获取关联然后需要访问它时,会发生N + 1查询问题:
这会生成以下SQL语句:
首先,Hibernate执行JPQL查询,并获取
然后,对于每个
因为 首先,您需要正确的SQL日志记录和监视,以便您可以发现此问题。 其次,这种问题最好是通过集成测试来捕获。您可以使用自动JUnit断言来验证生成的SQL语句的预期计数。 db-unit项目已经提供了这个功能,它是开源的。 当您确定N + 1查询问题时,需要使用JOIN FETCH以便在一个查询中提取子关联,而不是N.如果需要获取多个子关联,最好在初始查询中获取一个集合第二个带有辅助SQL查询。 在我看来,用Hibernate陷阱写的文章:为什么关系应该是懒惰的,与真正的N + 1问题正好相反。 如果您需要正确的解释,请参阅Hibernate - 第19章:提高性能 - 获取策略
提供的链接有一个非常简单的n + 1问题示例。如果你将它应用于Hibernate,它基本上是在谈论同样的事情。查询对象时,将加载实体,但任何关联(除非另外配置)都将延迟加载。因此,一个查询根对象,另一个查询加载每个对象的关联。返回100个对象意味着一个初始查询,然后100个额外的查询以获得每个n + 1的关联。 http://pramatr.com/2009/02/05/sql-n-1-selects-explained/ 一位百万富翁有N辆车。你想得到所有(4)轮子。 一(1)个查询加载所有汽车,但是对于每个(N)汽车,提交单独的查询以加载车轮。 成本: 假设索引符合ram。 1 + N查询解析和规划+索引搜索和1 + N +(N * 4)板访问以加载有效载荷。 假设索引不适合ram。 最坏情况下的额外成本1 + N板加载索引。 摘要
瓶颈是板通道(在硬盘上每秒约70次随机访问) 发出1个查询返回100个结果比发出100个查询每个返回1个结果要快得多。
N + 1选择问题很痛苦,在单元测试中检测这种情况是有意义的。 只需在测试类中添加一个特殊的JUnit规则,并在测试方法上放置具有预期查询数量的注释:
正如其他人所说的更优雅的问题是,您要么拥有OneToMany列的笛卡尔积,要么您正在进行N + 1选择。无论是可能的巨大结果集还是分别与数据库聊天。
我很惊讶这没有被提及,但这是我如何解决这个问题...我做了一个半临时的id表。当你有
这并不适用于所有情况(可能甚至不是大多数情况)但如果你有很多子对象使得笛卡尔积会失控(即大量的
首先,将父对象ID作为批处理插入到ids表中。
现在,对于每个 然后你只需要定期清理ids表。 如果用户为某种批量处理选择说100个左右的不同项目,这也特别有效。将100个不同的ID放在临时表中。 现在,您正在执行的查询数量取决于OneToMany列的数量。 以Matt Solnit为例,假设您将Car和Wheels之间的关联定义为LAZY并且您需要一些Wheels字段。这意味着在第一次选择之后,休眠将执行"从车轮中选择* car_id =:id"FOR FOR EACH Car。 这使得第一个选择和每个N车选择更多1,这就是为什么它被称为n + 1问题。 为避免这种情况,请将关联提取视为急切,以便hibernate通过连接加载数据。 但是注意,如果很多次你没有访问相关的车轮,最好保持LAZY或用Criteria更改提取类型。 |
最新内容
相关内容
linux无效对象的命令?
linux无效对象的命令?,软件,系统,单位,网络,管理,术语,检测,电脑,环境,风险,linux疑问:普通用户的ifconfig命令无法执行,如何解决?建议这个操linux数据库选择命令?
linux数据库选择命令?,系统,地址,工作,软件,管理,信息,工具,基础,命令,服务,操作系统常用命令和linux常用命令怎么学首先打开linux操作系统在学编程为什么选择Python
学编程为什么选择Python,数据,人工智能,标准,代码,发展,工资,占比,项目,待遇,培训,人工智能的飞速发展推动使用Python编程语言的人越来越多,Pypython如何选择Python中的IDE?
python如何选择Python中的IDE?,工具,代码,平台,通用,培训,实时,最新,智能,工作,环境,在写Python代码时,最好的方式就是使用集成开发环境了,也就报名Python培训选择哪种方式比较好
报名Python培训选择哪种方式比较好?,培训,工作,基础,时间,情况,在线,最划算,环境,系统,方式,Python不仅是一种高级的编程语言,而且还是一种应用pythonPandas Series对象有哪些属
pythonPandas Series对象有哪些属性?,工具,数据,数字,分析,培训,属性,数据类型,数组,对象,索引,想必大家都知道pandas,它是基于Numpy的一种工具python是面向对象还是面向过程的
python是面向对象还是面向过程的,数据,代码,基础,形态,培训,术语,设计,对象,过程,语言,Python虽然是解释型语言,但从设计之初就已经是一门面向实现Python对象的持久化存储
实现Python对象的持久化存储,数据,对象,系统,函数,培训,标准,环境,管理,参数,文件,Python中有个序列化过程叫作pickle,它能够实现任意对象与文python之访问对象的元数据
python之访问对象的元数据,数据,异常,培训,属性,模块,对象,类型,定义,文档,实例,当你对一个你构造的对象使用dir()时,可能会发现列表中的很多python之执行对象
python之执行对象,代码,数据,信息,概念,培训,通用,对象,属性,方法,赋值,有时候我们会碰到这样的需求,需要执行对象的某个方法,或是需要对对象的Python之数据库游标对象详解
Python之数据库游标对象详解,软件,代码,一致,培训,数据库,游标,对象,方法,事务,操作,常用数据库:MySQL、Oracle、SQLite但是包嵌入到Python中为什么要选择python?
为什么要选择python?,代码,工作,概念,设备,数据,宏观,年度,人工智能,教育,科技,作为新手,在面对广泛应用于企业级应用开发的Java、游戏客户端