Django annotate()性能 - 它应该超级慢吗?

时间:2013-07-08 00:33:07

标签: python django database-design django-models django-orm

annotate()使爬行缓慢变得正常吗?

使用这样的注释:

post_list = j.post_set.all().annotate(num_comments=Count('comment')).order_by('-pub_date')

不做注释需要花费四倍的时间:

post_list = j.post_set.all().order_by('-pub_date')

我也尝试过使用values()和defer(),但这些也没用。唯一真正的选项是将注释数量保留为Post表中的字段吗?

我顺便使用MySQL。

4 个答案:

答案 0 :(得分:2)

查看查询可能运行缓慢的原因的一种方法是实际查看生成的SQL。

最简单的方法是在django shell中执行:

>>> j.post_set.all().annotate(num_comments=Count('comment')).order_by('-pub_date')
>>> print j.query

您可能会发现重新排序链式查询集方法可能会改变性能:

>>> j.post_set.order_by('-pub_date').annotate(num_comments=Count('comment'))

答案 1 :(得分:1)

因此,对于post_list = j.post_set.all().order_by('-pub_date')的简单情况,您正在针对单个表对数据库进行查询。这会很快,如果在pub_date字段上添加索引,速度会更快。并在帖子的Meta部分设置ordering = ['-pub_date']

对于作为注释的更复杂的情况,对于您要求的每条记录,它需要在相关表中进行查找并计算返回的条目数。这将需要比简单情况更长的时间,即使它直接从索引中提取数据。

要进行问题排查,我建议您安装django-debug-toolbar,查看其查询标签,查找减慢速度的查询,然后运行explain工具以获取有关哪些因素减慢的额外信息下。此工具将向您显示生成的确切SQL,以及查询的哪些部分“成本”最高。

如果它让您慢慢爬行,也许您应该考虑将此信息存储在缓存中。或者使用像postgres这样的数据库,它允许您将这种元信息存储在自定义索引中。

答案 2 :(得分:1)

我知道这已经很晚了,但试着去吧:

j.post_set.all().extra({'num_comments': 'SELECT COUNT(*) FROM Comment WHERE Comment.post_id = post.id'}).order_by('pub_date')

(更改表名)

它比使用注释产生更快的查询,至少在postgres 9.2+上。

Annotate尝试使用左外连接然后分组生成最终结果,而上面的extra子句在所选列上添加子查询。后者可以允许数据库更好地利用现有的数据库索引。

答案 3 :(得分:1)

我知道这有点晚了,但我最终试图用Count()来解决类似的问题。我使用了Matthew Schinckel的想法,即使用shell来查找正在执行的SQL。然后,我打开了pgAdmin,粘贴了查询并执行了。令我惊讶的是,执行查询是我的Django应用程序占用时间的狮子。

我的应用程序使用几何字段,并且不在我的查询集中列出必需的字段,Group By子句包括所有表字段 - 包括一个非常大的geom字段。

在查看QuerysetAPI参考后,我在查询集中包含.Values(),其中包含一个简短的字段列表。这将我的查询时间从5秒减少到大约10毫秒!