Django prefetch_related 优化查询但仍然很慢

Luc*_*ler 6 python django postgresql prefetch

我在prefetch_related具有 5 m2m 字段的模型上遇到了一些严重的性能问题,并且我正在预取一些嵌套的 m2m 字段。

class TaskModelManager(models.Manager):
    def get_queryset(self):
        return super(TaskModelManager, self).get_queryset().exclude(internalStatus=2).prefetch_related("parent", "takes", "takes__flags", "assignedUser", "assignedUser__flags", "asset", "asset__flags", "status", "approvalWorkflow", "viewers", "requires", "linkedTasks", "activities")


class Task(models.Model):
    uuid = models.UUIDField(primary_key=True, default=genOptimUUID, editable=False)
    internalStatus = models.IntegerField(default=0)
    parent = models.ForeignKey("self", blank=True, null=True, related_name="childs")
    name = models.CharField(max_length=45)
    taskType = models.ForeignKey("TaskType", null=True)
    priority = models.IntegerField()
    startDate = models.DateTimeField()
    endDate = models.DateTimeField()
    status = models.ForeignKey("ProgressionStatus")
    assignedUser = models.ForeignKey("Asset", related_name="tasksAssigned")
    asset = models.ForeignKey("Asset", related_name="tasksSubject")
    viewers = models.ManyToManyField("Asset", blank=True, related_name="followedTasks")
    step = models.ForeignKey("Step", blank=True, null=True, related_name="tasks")
    approvalWorkflow = models.ForeignKey("ApprovalWorkflow")
    linkedTasks = models.ManyToManyField("self", symmetrical=False, blank=True, related_name="linkedTo")
    requires = models.ManyToManyField("self", symmetrical=False, blank=True, related_name="depends")

    objects = TaskModelManager()
Run Code Online (Sandbox Code Playgroud)

查询次数很好,数据库查询时间也很好,例如,如果我查询模型的 700 个对象,我有 35 个查询,平均查询时间为 100~200 毫秒,但总请求时间约为 8 秒。

丝绸时代

我进行了一些分析,它指出超过 80% 的时间都花在了prefetch_related_objects通话上。

剖析

我正在使用Django==1.8.5djangorestframework==3.4.6

我愿意以任何方式优化它。在此先感谢您的帮助。


编辑select_related

我已经尝试了 Alasdair 提出的改进

class TaskModelManager(models.Manager):
    def get_queryset(self):
        return super(TaskModelManager, self).get_queryset().exclude(internalStatus=2).select_related("parent", "status", "approvalWorkflow", "step").prefetch_related("takes", "takes__flags", "assignedUser", "assignedUser__flags", "asset", "asset__flags", "viewers", "requires", "linkedTasks", "activities")
Run Code Online (Sandbox Code Playgroud)

对于具有 32 个查询和 150 毫秒查询时间的请求,新结果仍然是 8 秒。


编辑 :

似乎 4 年前在 Django 问题跟踪器上打开了一张票,现在仍然打开:https://code.djangoproject.com/ticket/20577

小智 8

我遇到了同样的问题。

根据您链接问题,我发现您可以prefetch_related使用Prefetch对象和to_attr参数来提高性能。

从引入对象的提交Prefetch

当 Prefetch 实例指定 to_attr 参数时,结果存储在列表中而不是 QuerySet 中。这具有明显更快的幸运结果。性能的提高是由于我们节省了创建 QuerySet 实例的成本。

所以我通过调用显着改进了我的代码(从大约 7 秒到 0.88 秒):

Foo.objects.filter(...).prefetch_related(Prefetch('bars', to_attr='bars_list'))
Run Code Online (Sandbox Code Playgroud)

代替

Foo.objects.filter(...).prefetch_related('bars')
Run Code Online (Sandbox Code Playgroud)