【问题标题】:Django annotate() performance - is it supposed to be super slow?Django annotate() 性能 - 它应该超级慢吗?
【发布时间】:2013-07-05 06:16:14
【问题描述】:

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() ,但这些也没有帮助。将 cmets 的数量保留为 Post 表中的一个字段是唯一真正的选择吗?

顺便说一下,我正在使用 MySQL。

【问题讨论】:

  • I am using MySQL by the way. 你的问题 ;)
  • 您能详细说明一下吗?有很多大网站使用 MySQL...
  • 我在拿 MySQL 开玩笑,因为它有很多缺点和失败。是的,许多大型网站仍然使用 MySQL,但这并不意味着这是一个好的决定。见:grimoire.ca/mysql/choose-something-else
  • 谢谢。我想现在切换到 Postgres 还为时不晚 :)

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


【解决方案1】:

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

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

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

【讨论】:

    【解决方案2】:

    我知道这已经晚了,但请尝试:

    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 快得多。

    Annotate 尝试使用左外连接,然后通过分组来生成最终结果,而上面的额外子句将子查询添加到选定的列中。后者可以让数据库更好地利用现有的数据库索引。

    【讨论】:

    • 谢谢,这很有效,但它很可怕,而且完全不适合使用 django'y。有没有办法在不输入显式表名的情况下做到这一点?
    • MyTable._meta.db_table 可用于从对象中获取表名。对元 API 的类似调用可用于获取列名等。
    • 您可以使用子查询的子类以更惯用的方式执行此操作。 j.post_set.annotate(num_comments=SubqueryCount(...))schinckel.net/2019/07/30/subquery-and-subclasses 有更多细节。
    【解决方案3】:

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

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

    对于故障排除,我建议您安装django-debug-toolbar,查看它的查询选项卡,找到使您速度变慢的查询,然后运行explain 工具以获取有关使您速度变慢的额外信息。此工具将向您显示生成的确切 SQL,以及查询的哪些部分“成本”最高。

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

    【讨论】:

    【解决方案4】:

    了解查询为何运行缓慢的一种方法是实际查看生成的 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'))
    

    【讨论】:

      猜你喜欢
      • 2015-03-17
      • 2014-06-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-07-20
      • 2021-03-07
      • 1970-01-01
      相关资源
      最近更新 更多