这是我的模型的伪代码
Offer
match = FK(Match)
Odds
offer = FK(Offer, related_name='odds')
test1 = "[o.odds.all() for o in Offer.objects.filter(match__id=123)]"
test2 = "[o.odds.all() for o in Offer.objects.filter(match__id=123).prefetch_related('odds')]"
timeit.timeit(test1, number=1000)
>>> 3.078
timeit.timeit(test2, number=1000)
>>> 11.794
我认为在预取赔率时,显示它们根本不会有任何问题 - 然后事实证明它实际上预取它们的速度较慢。我做错了什么,或者我根本不理解使用prefetch_related
?
由于
修改
我将查询o.odds.latest()
并仅使用该信息,而我只是想优化我的查询。我只是很难缠绕select_related
和prefetch_related
。
答案 0 :(得分:3)
由于你使用的是FK,你可以使用select_related而不是prefetch_related,看看它是如何比较的:
select_related通过创建SQL连接并在SELECT语句中包含相关对象的字段来工作。因此,select_related在同一数据库查询中获取相关对象。但是,为了避免加入“多”关系会产生更大的结果集,select_related仅限于单值关系 - 外键和一对一。
另一方面,prefetch_related对每个关系进行单独查找,并在Python中进行“加入”。这允许它预取多对多和多对一对象,除了select_related支持的外键和一对一关系之外,这些对象无法使用select_related完成。它还支持预取GenericRelation和GenericForeignKey。
答案 1 :(得分:2)
如果你检查Django执行的查询,你会看到prefetch_related()
导致带有WHERE IN (ids)
子句的附加查询。然后,这些值将映射回Python中的原始对象,从而防止额外的数据库负载。
针对您的整个数据库在无限制prefetch_related()
QuerySet上调用all()
可能非常代价高昂,尤其是在您的表很大的情况下。但是,如果您使用较小的数据集,这要归功于对原始QuerySet的有意义的filter(key=value)
添加,prefetch_related()
将减少N次到数据库的次数减少到数据库的1次。这可能会导致性能大幅提升。
作为旁注 - 除非您需要这些数据,否则不要运行prefetch_related("odds")
。您的测试没有使用额外的信息,但它仍然需要额外的工作来获取它。没有免费的午餐:)