Django:如何有效地使用 select_lated() 和 Paginator?

问题描述 投票:0回答:2

我有 2 个相关模型,每个模型有 1000 万行,想要对其中一个模型的 50 000 个项目执行高效的分页请求,并访问另一个模型上的相关数据:

class RnaPrecomputed(models.Model):
    id = models.CharField(max_length=22, primary_key=True)
    rna = models.ForeignKey('Rna', db_column='upi', to_field='upi', related_name='precomputed')
    description = models.CharField(max_length=250)


class Rna(models.Model):
    id = models.IntegerField(db_column='id')
    upi = models.CharField(max_length=13, db_index=True, primary_key=True)
    timestamp = models.DateField()
    userstamp = models.CharField(max_length=30)

如您所见,

RnaPrecomputed
通过外键与
RNA
相关。现在,我想获取 50 000 个
RnaPrecomputed
项目以及与它们相关的相应
Rna
的特定页面。如果我在没有
select_related()
调用的情况下执行此操作,我预计会出现 N+1 请求问题。时间安排如下:


首先,作为参考,我根本不会碰相关模型:

rna_paginator = paginator.Paginator(RnaPrecomputed.objects.all(), 50000)
message = ""
for object in rna_paginator.page(400).object_list:
    message = message + str(object.id)

需要:

real    0m12.614s
user    0m1.073s
sys 0m0.188s

现在,我将尝试访问相关模型上的数据:

rna_paginator = paginator.Paginator(RnaPrecomputed.objects.all(), 50000)
message = ""
for object in rna_paginator.page(400).object_list:
    message = message + str(object.rna.upi)

需要:

real    2m27.655s
user    1m20.194s
sys 0m4.315s

数量很多,所以,可能我有 N+1 请求问题。


但是现在,如果我使用

select_related()

rna_paginator = paginator.Paginator(RnaPrecomputed.objects.all().select_related('rna'), 50000)
message = ""
for object in rna_paginator.page(400).object_list:
    message = message + str(object.rna.upi)

还需要更多:

real    7m9.720s
user    0m1.948s
sys 0m0.337s

所以,不知怎的,

select_related()
让事情变慢了三倍,而不是让它们变得更快。可能没有它,我有 N+1 个请求,因此对于每个
RnaPrecomputed
条目,Django ORM 可能必须向数据库执行额外的请求以获取相应的
Rna

我做错了什么以及如何使

select_related()
在分页查询集上表现良好?

sql django orm
2个回答
2
投票

值得检查一下数据库中是否缺少索引。您的

db_index=True
字段有
Rna.upi
,但您确定该索引存在于数据库中吗?

如果

select_related
使
count()
查询变慢,那么您可以尝试在分页
select_related
上执行
object_list

for object in rna_paginator.page(300).object_list.select_related():
    message = message + str(object.rna.upi)

0
投票

只是用我自己的经验来解决这个问题。如果您正在处理无法访问分页器来执行 select_lated after 分页(即在管理列表显示上)的情况,则使用 prefetch_lated 可能会更高效。我对 SQL 的理解不是那么深,但我相信这是因为 select_lated 中的左连接在 LIMIT 之前执行,如果表中有很多行,这可能会非常昂贵。以下是一些屏幕截图,比较了我的一个管理视图使用选择与预取的性能。正在查询的表有 120 万行,链接表有 20 行。 选择相关 预取相关

© www.soinside.com 2019 - 2024. All rights reserved.