【问题标题】:How to retrieve count objects faster on Django?如何在 Django 上更快地检索计数对象?
【发布时间】:2021-09-13 14:20:23
【问题描述】:

我的目标是优化 Django 模型中对象计数的检索。

我有两个模型:

  • 用户
  • 前景

这是一对多的关系。一个用户可以创建多个前景。一个潜在客户只能由一个用户创建。

我正在尝试获取用户在过去 24 小时内创建的潜在客户。

Prospects 模型在我的 PostgreSQL 数据库中大约有 700 万行。用户只有 2000 个。

我当前的代码需要很长时间才能获得所需的结果。

我尝试使用filter()count()

import datetime

# get the date but 24 hours earlier
date_example = datetime.datetime.now() - datetime.timedelta(days = 1)

# Filter Prospects that are created by user_id_example
# and filter Prospects that got a date greater than date_example (so equal or sooner)
today_prospects = Prospect.objects.filter(user_id = 'user_id_example', create_date__gte = date_example)

# get the count of prospects that got created in the past 24 hours by user_id_example
# this is the problematic call that takes too long to process
count_total_today_prospects = today_prospects.count()

我在工作,但需要太多时间(5 分钟)。因为它检查的是整个数据库,而不仅仅是检查,我认为它会:只检查用户在过去 24 小时内创建的潜在客户。

我也尝试过使用 annotate,但它同样慢,因为它最终做的事情与普通的 .count() 相同:

today_prospects.annotate(Count('id'))

如何以更优化的方式获得计数?

【问题讨论】:

  • 如果为create_date 字段添加db_index=True 会怎样?添加后需要先迁移数据库。
  • 目前我的字段是:create_date = models.DateTimeField(auto_now = True) 我应该把它改成create_date = models.DateTimeField(auto_now = True, db_index = True) 然后呢?同样的.count 方法应该工作得更快吗? @WillemVanOnsem
  • @RobZ 不,您仍然需要使用适当的列索引更新您的数据库。

标签: python django postgresql


【解决方案1】:

假设您还没有它,我建议添加一个包含用户和日期字段的索引(确保它们按此顺序排列,首先是用户,然后是日期,因为对于您正在寻找的用户对于完全匹配,但对于日期,您只有一个起点)。这应该会加快查询速度。

例如:

class Prospect(models.Model):
    ...

    class Meta:
        ...
        indexes = [
            models.Index(fields=['user', 'create_date']),
        ]
        ...

这应该会创建一个新的迁移文件(运行 makemigrationsmigrate),它将索引添加到数据库中。

之后,您的相同代码应该会运行得更快一些:

count_total_today_prospects = Prospect.objects\
    .filter(user_id='user_id_example', create_date__gte=date_example)\
    .count()

【讨论】:

  • 你的索引方法和create_date = models.DateTimeField(auto_now = True, db_index = True)之间有区别吗?
  • @RobZ 是的,有区别。我的版本创建了一个索引,该索引以所需的顺序保存两个字段,这使得查询更快。在您的版本中(2 个单独的索引,一个在用户字段上,一个在日期字段上),数据库只能使用这些索引之一,不能同时使用两者。
  • @RobZ 一个只有一个字段的索引肯定比没有索引要好,但是两个字段的索引(按照正确的顺序,就像我在答案中所说的那样)会更好
【解决方案2】:

Django 的文档:

count() 调用在后台执行 SELECT COUNT(*),因此您应该始终使用 count() 而不是将所有记录加载到 Python 对象中并在结果上调用 len()(除非您需要无论如何将对象加载到内存中,在这种情况下 len() 会更快)。 请注意,如果您想要 QuerySet 中的项目数并且还从中检索模型实例(例如,通过迭代它),使用 len(queryset) 可能更有效,它不会导致额外的数据库查询,例如count() 会。 如果查询集已被完全检索,count() 将使用该长度而不是执行额外的数据库查询。

看看这个链接:https://docs.djangoproject.com/en/3.2/ref/models/querysets/#count

尝试使用 len()。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2022-09-28
    • 2014-09-28
    • 2012-02-13
    • 1970-01-01
    • 2010-11-08
    • 1970-01-01
    • 2021-09-13
    • 1970-01-01
    相关资源
    最近更新 更多