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。
答案 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毫秒!