Django ORM - 带M2M的加速过滤器

时间:2014-08-01 16:30:23

标签: django orm

我正在使用Django 1.4,Python 2.7,Ubuntu 14.04和PostgreSQL 9.3。

我有两个通过多对多(M2M)关系相关的模型。在针对M2M关系进行过滤时,我遇到了重大的性能问题。

class Meat(models.Model):
    name1 = models.CharField(max_length=200)
    name2 = models.CharField(max_length=200)

class Potato(models.Model):
    bad_meats = models.ManyToManyField(
        Meat, null=True, blank=True, related_name="bad_potatoes")

我在Meat查询集上链接过滤器。

potato = Potato.objects.get(pk=12345)
qs = Meat.objects.all()
qs = qs.filter(name1='foo')
qs = qs.filter(name2='bar')
qs = qs.exclude(id__in=potato.bad_meats.all())

使用__in会大大减慢此过滤过程。有没有其他方法可以建议加快这一点?

我无法使用.raw()查询,因为我需要根据特定条件在整个过程中链接过滤器。

要提供更多详细信息,Meat表格大约有150,000行,potato.bad_meats.all()大约有40,000个结果。此查询目前大约需要8-10秒。我需要将其降至1秒以下。

1 个答案:

答案 0 :(得分:3)

150K行和40K关系似乎不是那么大需要8s,也许你的机器有一个低CPU?

以下是您可以查看/尝试的一些内容:

  • 您是否检查了数据库结构中相关列的索引和外键?
  • 您是否尝试在name1和name2列上添加索引?
  • 您还应该尝试监视python代码在原始结果和Model之间进行映射所花费的时间,它可能比查询时间更重要。您可以尝试使用valuesvalues_list来获取dict / list而不是Model对象。

关于db查询,我发现了一个稍快的查询(但在我的机器上,这并不重要)。 我创建了一个带有150K肉类和1个与40K肉类相关的马铃薯的数据库。使用与您相同的方法在我的机器上使用2.3Gz运行不到1秒。

我打印了这样生成的查询

>>> qs = Meat.objects.filter(name1='foo').filter(name2='bar').exclude(id__in=potato.bad_meats.all())
>>> print qs.query
SELECT "coucou_meat"."id", "coucou_meat"."name1", "coucou_meat"."name2" 
FROM "coucou_meat" 
WHERE ("coucou_meat"."name1" = foo  AND "coucou_meat"."name2" = bar 
AND NOT ("coucou_meat"."id" IN
    (SELECT U0."id" 
    FROM "coucou_meat" U0 
    INNER JOIN "coucou_potato_bad_meats" U1 ON (U0."id" = U1."meat_id") 
    WHERE U1."potato_id" = 1 )))

我试图直接在pgadmin中运行它,查询花了325ms。

然后我尝试了一个稍微不同的查询:

>>> qs = Meat.objects.filter(name1='foo').filter(name2='bar').exclude(bad_potatoes__id=12345)
>>> print qs.query
SELECT "coucou_meat"."id", "coucou_meat"."name1", "coucou_meat"."name2" 
FROM "coucou_meat" WHERE ("coucou_meat"."name1" = foo  AND "coucou_meat"."name2" = bar  
AND NOT (("coucou_meat"."id" IN 
    (SELECT U1."meat_id" 
    FROM "coucou_potato_bad_meats" U1 
    WHERE (U1."potato_id" = 1  AND U1."meat_id" IS NOT NULL)) 
        AND "coucou_meat"."id" IS NOT NULL)))

在pgadmin中运行它并获得230ms的执行时间。由于它有点快,你可以尝试看它是否有任何区别。