优化 Django 获取查询

Posted

技术标签:

【中文标题】优化 Django 获取查询【英文标题】:Optimizing Django get queries 【发布时间】:2016-07-15 12:31:04 【问题描述】:

我有类似这样的 Django 代码:

for obj in some_list:
    m1obj = Model1.objects.get(a=obj.a, b=obj.b, c=obj.c)
    Model2(m1=m1obj, d=obj.d, e='foo').save()

我确实使用 bulk_create 优化了对 Model2 的插入,但是,由于来自 Model1get,这仍然非常缓慢(对于 3k 插入,~45sec)。

我也尝试添加:

class Meta:
    index_together = [
        ('a', 'b', 'c'),
    ]
    unique_together = [
        ('a', 'b', 'c'),
    ]

unique_together 有点帮助,index_together 似乎没有太大作用。

我有一个麻烦的解决方法:

    过滤Model1 获取我需要的所有对象,按一个或多个键排序,例如order_by('a', 'b'),并确保 Django 缓存结果,例如len() 使用二分搜索 (from bisect import bisect_left) 来定位第一个 a,然后是 b ... 等等(虽然 bs 和 cs 的数量要少得多,所以只是迭代是一样的。李>

这将插入时间缩短到3秒

必须有更好、更清洁和可维护的方法来做到这一点。有什么建议? 有没有办法在 Django 的缓存查询结果中过滤/获取(智能)?

编辑:将 d='foo' 更改为 d=obj.d - 任何批量获取都需要映射到它所属的元组,否则我无法创建 Model2 条目。

【问题讨论】:

【参考方案1】:

您可以进行单个查询(如 here 所述),该查询将仅获取您需要的结果,因此无需稍后进行排序和二分搜索。

我没有测试过它,所以我不知道它是否会比你已经在做的更快。此外,由于 SQL 查询将很大(根据 some_list 中的记录数),因此如果超过参数 max_allowed_packetmysql 设置中定义的大小,此查询可能会引发错误(默认为 16MB,如 here 所述)。

import operator
from django.db.models import Q
query = reduce(operator.or_, (Q(a=obj.a, b=obj.b, c=obj.c) for x in values))
model1_objs = Model1.objects.filter(query)

然后你可以用Model2bulk_create

Model2.objects.bulk_create([
    Model2(m1=m1, d='foo', e='bar')
    for m1 in model1_objs
])

【讨论】:

不幸的是,您的建议比按顺序执行get() 要慢得多。我在 3 多分钟后停止了它。此外,正如我在编辑中提到的,如果参数“d”和“e”是恒定的,这可能(应该?)起作用。由于它们不是,我无法将model1_objs 映射到正确的obj.d,因为数据库查询不能保证顺序。【参考方案2】:

Model1 有多少行?如果它相对较小(小于 50k),您可以使用 filter 获取所有内容,然后比较 python 中的元组。

“some_list”是小列表(小于100)怎么样,如果是你可以Q关键字一次过滤所有内容。

first = some_list.pop()
conditions = Q(a=first.a, b=first.b, c=first.c)
for obj in some_list:
    conditions |= Q(a=obj.a, b=obj.b, c=obj.c)

Model1.objects.filter(conditions)   # this will get your all the Model1 from ur list

Q 对象参考:https://docs.djangoproject.com/en/1.9/ref/models/querysets/#q-objects

【讨论】:

此解决方案与@muhammad-tahir 建议的相同。对于 3K 行,我在几分钟后终止了查询。对于较小的组,它并不比单独的查询快得多(根本没有)。我确实改进了创建元组到表行映射的解决方法,它既快速又可读,但如果行是 100K 而不是 3K,我可能会遇到内存问题......

以上是关于优化 Django 获取查询的主要内容,如果未能解决你的问题,请参考以下文章

用于迭代嵌套结果的 Django 查询集预取优化

Django 第二十三篇优化查询

django_orm查询性能优化

Django 07

使用 django ORM 获取只有最新孩子的父母的优化方式

Django中的数据库查询