Python中Django框架的order_by与select_related连用导致的性能问题如何解决?

数据库中有几张表通过外键关联的 现在我想将一些数据排序展示出来(涉及了几张表)

然后就碰到了如下情况

第一种:

orders = Order.objects.filter(**params).order_by('-created')

然后在模板中遍历,会导致查询很多次数据库

第二种:

orders = Order.objects.filter(**params).order_by('-created').select_related("source", "customer", "handler","source__handler")

这样减少查询次数但是查询时间超级久

第三种: 我将第二种里面的 order_by 取消掉后,也就是

orders = Order.objects.filter(**params).select_related("source", "customer", "handler","source__handler")

这种情况,查询效率就又正常了,可是数据不是排序的

然后在我百度了一下午之后发现这个博客写的好像和我的情况一样,链接

可是问题来了,我只会写简单 sql,这个 sql 实在是太复杂了,搞不定,请教下各位前辈,有没有办法直接用 ORM 就能解决


Python中Django框架的order_by与select_related连用导致的性能问题如何解决?

10 回复

可以分成两步,先 order by 获取 ID 和 created,然后在关联查询和排序😀


这个问题很典型。order_byselect_related连用导致性能问题,通常是因为order_by排序的字段不在select_related预加载的主表上,而是关联表的字段。这会导致数据库无法高效利用JOIN,甚至可能产生大量临时数据排序,拖慢查询。

核心原因和解决方案如下:

1. 问题根源 当你写 Model.objects.select_related('foreign_key').order_by('foreign_key__field') 时,如果关联表数据量大,数据库需要对JOIN后的结果集进行排序,这个排序操作(filesort)可能非常耗时,尤其是在没有合适索引的情况下。

2. 解决方案

方案A:优化索引(治本) 确保被排序的关联表字段(foreign_key.field)上有索引。这是最有效的办法。

# 在关联模型上,例如RelatedModel的'field'字段添加数据库索引
class RelatedModel(models.Model):
    field = models.CharField(max_length=100, db_index=True)  # 添加db_index
    # ... 其他字段

方案B:调整查询逻辑(治标) 如果无法加索引或数据量极大,可以拆分查询,利用Python进行排序。注意:这只在结果集相对较小时可行,否则内存压力大。

# 先预加载,再在Python中排序
queryset = Model.objects.select_related('foreign_key').all()
sorted_list = sorted(queryset, key=lambda x: x.foreign_key.field if x.foreign_key else '')
# 此时sorted_list是列表,不是QuerySet

方案C:使用Prefetch进行更精细的控制(高级) 如果关联关系复杂(如反向关联或多对多),使用prefetch_related配合Prefetch对象可能比select_related更合适,但order_by问题依然需要结合索引解决。

from django.db.models import Prefetch
queryset = Model.objects.prefetch_related(
    Prefetch('foreign_key', queryset=RelatedModel.objects.only('id', 'field'))
).order_by('foreign_key__field')  # 排序问题依旧,仍需索引

总结建议 优先给关联表的排序字段加数据库索引。

你可以把 select_related 改成 prefetch_related. 应该可以完美解决你的问题.

created 有索引吗?

有索引的 而且也生效了

感谢 我去尝试下

感谢 我去尝试下


#4
explain 打印下执行计划看看

感谢帮助 我确定不是索引的问题哈 问题就是我贴的链接里面的那个问题 已经解决 十分感谢

是怎么解决的呢

回到顶部