【问题标题】:Optimizing a slow django queryset from a large and growing dataset从庞大且不断增长的数据集中优化慢速 django 查询集
【发布时间】:2015-12-16 16:48:03
【问题描述】:

我的页面加载速度太慢。不知何故,我需要改进查询数据的方式(缓存?部分加载/页面?等)

请注意,我是 django 菜鸟,并没有完全理解 model.Managermodels.query.QuerySet,所以如果这个设置看起来很尴尬......

目前,页面加载查询集大约需要 18 秒,并且目前只有大约 500 条记录。平均每天会有大约 100 条新记录。

数据库是Postgresql

缓慢的视图

def approvals(request):
    ...
    approved_submissions = QuestSubmission.objects.all_approved()
    ...

缓慢的查询集

class QuestSubmissionQuerySet(models.query.QuerySet):
    ...

    def approved(self):
        return self.filter(is_approved=True)

    def completed(self):
         return self.filter(is_completed=True).order_by('-time_completed')

    ...

class QuestSubmissionManager(models.Manager):
    def get_queryset(self):
        return QuestSubmissionQuerySet(self.model, using=self._db)

    def all_approved(self, user=None):
        return self.get_queryset().approved().completed()

    ...

QuestSubmission.objects.all_approved() 产生的 SQL:

'SELECT "quest_manager_questsubmission"."id", "quest_manager_questsubmission"."quest_id", "quest_manager_questsubmission"."user_id", "quest_manager_questsubmission"."ordinal", "quest_manager_questsubmission"."is_completed", "quest_manager_questsubmission"."time_completed", "quest_manager_questsubmission"."is_approved", "quest_manager_questsubmission"."time_approved", "quest_manager_questsubmission"."timestamp", "quest_manager_questsubmission"."updated", "quest_manager_questsubmission"."game_lab_transfer" FROM "quest_manager_questsubmission" WHERE ("quest_manager_questsubmission"."is_approved" = True AND "quest_manager_questsubmission"."is_completed" = True) ORDER BY "quest_manager_questsubmission"."time_completed" DESC'

模型

class QuestSubmission(models.Model):
    quest = models.ForeignKey(Quest)
    user = models.ForeignKey(settings.AUTH_USER_MODEL, related_name="quest_submission_user")
    ordinal = models.PositiveIntegerField(default = 1, help_text = 'indicating submissions beyond the first for repeatable quests')
    is_completed = models.BooleanField(default=False)
    time_completed = models.DateTimeField(null=True, blank=True)
    is_approved = models.BooleanField(default=False)
    time_approved = models.DateTimeField(null=True, blank=True)
    timestamp = models.DateTimeField(auto_now=True, auto_now_add=False)
    updated = models.DateTimeField(auto_now=False, auto_now_add=True)
    game_lab_transfer = models.BooleanField(default = False, help_text = 'XP not counted')

    class Meta:
        ordering = ["time_approved", "time_completed"]

    objects = QuestSubmissionManager()

    #other methods
    ....

有什么策略可以解决这个问题?我尝试使用 django 的 Paginator,但它似乎只显示在页面中,但它仍然加载整个查询集。

【问题讨论】:

  • 您是否尝试过使用manage.py shellstr(QuestSubmission.objects.all_approved().query()) 来查看它实际生成的SQL?否则代码看起来很正常,问题可能出在其他地方。
  • 我得到:TypeError: 'Query' object is not callable
  • 哦,对不起,应该只是str(QuestSubmission.objects.all_approved().query)
  • 您的浏览器计时并不能真正告诉您服务器的瓶颈在哪里。您可以尝试添加django-debug-toolbar,这将分解服务器端的执行时间并显示哪些查询正在访问数据库。

标签: python django performance postgresql django-queryset


【解决方案1】:

如果您在页面中显示相关对象,请尝试使用 select_related()

如果没有 select_related(),这将为每个数据库查询 循环迭代以获取每个条目的相关博客。

【讨论】:

  • 我想这可能是它。我尝试在 shell 中运行查询,它立即返回。该模板适用于approved_submissions.quest.etc 和approved_submissions.user.etc。正在调查……
  • 哦,我是个白痴。它很快,因为我在没有数据的开发服务器上尝试过......
  • @43Tesseracts 这应该是一个问题,因为您正在迭代相关记录。请记住,它会在之前为每条记录的每个关系执行一个数据库查询......只会增长很多!尝试启用 debug_toolbar 以了解有关服务器上发生的事情的更多详细信息。
【解决方案2】:

首先要看的:

  • 此查询是否因为返回的结果集非常大而变慢?

  • 此查询是否因为过滤表需要一些时间而变慢?

假设是前者,除了“返回更少的数据”之外,你没有很多好的选择。

如果是后者,您可能应该在数据库上运行EXPLAIN,但马上我会说您可能想要一个索引,可能在(is_approved, is_completed) 上。可以通过以下方式完成:

class Meta:
    index_together = [
        ["is_completed", "is_approved"],
    ]

【讨论】:

  • 有没有办法检查它是否是其中之一?
【解决方案3】:

这更多是浏览器问题而不是数据库问题。使用 Django 分页,您可以一次选择一页并降低数据库和浏览器的负载:Django Pagination

【讨论】:

  • 我没有看到降低数据库负载的任何地方?在示例中,他们仍在加载整个查询集:contact_list = Contacts.objects.all()。但是,如果性能问题是由于 Paulo P 建议的模板中相关数据的命中,那么它应该可以正常工作吗?
  • 为什么你认为是浏览器?我以为绿线(17.71 秒)是 django 加载视图/模板?
  • 是的,但这也包括在浏览器中呈现页面。当您使用分页时,您一次只能选择 20 条(如果您的页面大小为 20 条)记录,并且您可以从数据库中获得更快的响应。另一个重要的性能问题是当您的数据集很大时,Django ORM 必须将从数据库中读取的每个项目转换为模型对象。
【解决方案4】:

我将构建一个基本查询集来构建并应用所需的过滤器:

def approvals(request):
    ...
    approved_submissions = QuestSubmission.objects.select_related('quest', 'user').all_approved()
    ...

【讨论】:

    猜你喜欢
    • 2022-01-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多