【问题标题】:Evaluate whole django queryset in one database hit在一个数据库命中评估整个 django 查询集
【发布时间】:2018-09-26 11:20:45
【问题描述】:

我正在寻找一种方法来评估 one 数据库命中中的整个 django 查询集。 我将 Django 2.0.8 与 Python 3.6.5 一起使用。
我曾经评估列出它们的查询集:

queryset = Model.objects.all()
# len(connection.queries) = 0
list(queryset)
# len(connection.queries) = 87
len(queryset) = 43

只执行一次数据库命中。但是现在,看起来这对数据库的访问次数超过了 n 次 -n 是查询集的长度 - 当 n 很大,数据库访问成本很高。

谢谢

【问题讨论】:

  • 这已经用 one hit 执行了。
  • 你有没有“消费”过相关的对象?
  • 我用以前的 django 版本做的时候就是这种情况,但现在看起来不像了。查看我的编辑
  • 你能看一下connection.queries吗?例如,分享最后 5 个,看看它们是否不是 related 对象的获取。
  • @WillemVanOnsem 你所说的“消费”相关对象是什么意思?

标签: python django


【解决方案1】:

简答:您的假设错误。如果您评估查询集,您将立即在一个查询中获取所有结果。

所以对于list(..),在您枚举查询集的地方,Django 将首先进行查询,使用该查询获取所有记录,然后反序列化所有行,并将其放入列表中.

但是存在线性行为,因为答案的大小将随记录数而变化,反序列化并将其放入列表中的工作量也将随行数而变化。

如果您的查询有 prefetch_related 调用,这也会导致一些额外的查询,但是这些(查询数量)通常会随着数量线性 prefeches,而不是记录数。

如果您打算获取大量外键,例如:

for obj in Model.objects.all():
    print(obj.some_reversefk.all())

然后这将查询 per 对象来获取 related 对象(外键指向的对象)。

为避免这种情况,您可以使用.select_related(..).prefetch_related(..).prefetch_related(..) 将进行一到两个额外查询,但会立即获取 所有 相关对象。所以你可以使用:

Model.objects.prefetch_related('some_m2m')

【讨论】:

  • 你是对的。只有一个查询针对原始模型,其余查询针对相关对象。那么我应该在此之前执行 select_related 还是 prefetch_related
  • @CharlesSiouti: 如果是多对一 (ForeignKey) 或一对一 (OneToOneField),您可以使用 select_related,用于所有可以充当multiplier 但是,您应该使用prefetch_related
【解决方案2】:

它实际上是在 database.read https://docs.djangoproject.com/en/2.0/topics/db/optimization/ 中的一次点击

即使您的模型具有相关字段,它也会在单个查询中获取您的非相关字段。要访问所有相关字段数据,请先阅读经典 N+1 查询问题,阅读https://medium.com/@hakibenita/things-you-must-know-about-django-admin-as-your-app-gets-bigger-6be0b0ee9614

顺便说一句,如果您的模型有太多对象,请避免在list() 中使用.all(),它会将所有对象加载到内存中。而是使用.count()

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-01-22
    • 1970-01-01
    • 1970-01-01
    • 2015-10-08
    • 1970-01-01
    • 1970-01-01
    • 2011-07-18
    • 2021-10-23
    相关资源
    最近更新 更多