使用预取相关的查询速度慢于没有?

时间:2014-08-07 10:18:52

标签: python django

这是我的模型的伪代码

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_relatedprefetch_related

2 个答案:

答案 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")。您的测试没有使用额外的信息,但它仍然需要额外的工作来获取它。没有免费的午餐:)