“N+1选择问题”在对象关系映射(ORM)讨论中通常被称为一个问题,我理解这与必须为对象世界中看似简单的东西进行大量数据库查询有关。
有人对这个问题有更详细的解释吗?
“N+1选择问题”在对象关系映射(ORM)讨论中通常被称为一个问题,我理解这与必须为对象世界中看似简单的东西进行大量数据库查询有关。
有人对这个问题有更详细的解释吗?
当前回答
因为这个问题,我们离开了Django的ORM。基本上,如果你尝试
for p in person:
print p.car.colour
ORM将很高兴地返回所有人(通常作为Person对象的实例),但随后需要为每个Person查询car表。
一种简单且非常有效的方法是我称之为“扇形折叠”的方法,它避免了来自关系数据库的查询结果应该映射回组成查询的原始表的荒谬想法。
步骤1:宽选择
select * from people_car_colour; # this is a view or sql function
这将返回类似
p.id | p.name | p.telno | car.id | car.type | car.colour
-----+--------+---------+--------+----------+-----------
2 | jones | 2145 | 77 | ford | red
2 | jones | 2145 | 1012 | toyota | blue
16 | ashby | 124 | 99 | bmw | yellow
第2步:客观化
将结果吸入通用对象创建器中,并在第三项之后添加一个要拆分的参数。这意味着“jones”对象不会被制作多次。
步骤3:渲染
for p in people:
print p.car.colour # no more car queries
有关python的扇形折叠的实现,请参阅此网页。
其他回答
在我看来,《Hibernate陷阱:为什么关系应该懒惰》中的文章与真正的N+1问题完全相反。
如果您需要正确的解释,请参阅Hibernate-第19章:提高性能-获取策略
选择提取(默认值)为极易受到N+1选择的影响问题,因此我们可能希望启用联接获取
以Matt Solnit为例,假设您将Car和Wheels之间的关联定义为LAZY,并且需要一些Wheels字段。这意味着在第一次选择后,休眠将为每辆车执行“select*from Wheels where car_id=:id”。
这使得每个N辆车的第一次选择和更多的1次选择,这就是为什么它被称为N+1问题的原因。
为了避免这种情况,请使关联获取变得急切,以便hibernate使用连接加载数据。
但请注意,如果您多次无法访问关联的Wheels,最好将其保持为LAZY或使用Criteria更改获取类型。
假设你有公司和雇员。公司有许多雇员(即雇员有一个字段COMPANY_ID)。
在某些O/R配置中,当您有一个映射的Company对象并访问其Employee对象时,O/R工具将为每个员工执行一次选择,如果您只是在直接SQL中执行操作,则可以从Company_id=XX的员工中选择*。因此,N(员工人数)加1(公司)
这就是EJB实体bean的初始版本是如何工作的。我相信像Hibernate这样的东西已经解决了这个问题,但我不太确定。大多数工具通常包含有关其映射策略的信息。
因为这个问题,我们离开了Django的ORM。基本上,如果你尝试
for p in person:
print p.car.colour
ORM将很高兴地返回所有人(通常作为Person对象的实例),但随后需要为每个Person查询car表。
一种简单且非常有效的方法是我称之为“扇形折叠”的方法,它避免了来自关系数据库的查询结果应该映射回组成查询的原始表的荒谬想法。
步骤1:宽选择
select * from people_car_colour; # this is a view or sql function
这将返回类似
p.id | p.name | p.telno | car.id | car.type | car.colour
-----+--------+---------+--------+----------+-----------
2 | jones | 2145 | 77 | ford | red
2 | jones | 2145 | 1012 | toyota | blue
16 | ashby | 124 | 99 | bmw | yellow
第2步:客观化
将结果吸入通用对象创建器中,并在第三项之后添加一个要拆分的参数。这意味着“jones”对象不会被制作多次。
步骤3:渲染
for p in people:
print p.car.colour # no more car queries
有关python的扇形折叠的实现,请参阅此网页。
在Phabricator文档中可以找到问题的简短解释:
N+1查询问题是一个常见的性能反模式。它看起来像这样:$cats=load_cats();foreach($cat作为$cat){$cats_hats=>load_hats_for_cat($cat);// ...}假设load_cats()的实现归结为:从猫的位置选择*。。。..和load_hats_for_cat($cat)的实现如下:SELECT*FROM hat WHERE catID=。。。..当代码执行时,您将发出“N+1”个查询,其中N是猫的数量:从猫的位置选择*。。。SELECT*FROM hat WHERE catID=1SELECT*FROM hat WHERE catID=2SELECT*FROM hat WHERE catID=3从帽子中选择*,其中catID=4...
解决方案:
发出一个返回100个结果的查询比发出发出100个查询,每个查询返回1个结果。
在迭代之前加载所有数据。