Многие в медленном темпе в Джанго - PullRequest
3 голосов
/ 13 марта 2019

У меня есть две простые модели:

class A(models.Model):
name_a = models.CharField(
    _("name_a"),
    max_length=255)

    b = models.ManyToManyField(
        'B',
        related_name='a',
        blank=True
    )

class B(models.Model):
    name_b = models.CharField(
        _("name_b"),
        max_length=255)

Я создал 1000 записей для A:

for i in range(1000):
    A.objects.create()

и 3 объекта для B:

for i in range(3):
    B.objects.create()

и соедините каждый Объект b с каждым объектом отношения a через m2m:

for a in A.objects.all().iterator(): 
    a.b.add(B.objects.all()[0]) 
    a.b.add(B.objects.all()[1]) 
    a.b.add(B.objects.all()[2])

Теперь для каждого объекта я хочу получить все b объектов:

import time 
start = time.time()
objects = A.objects.all()
for n in objects.iterator(): 
     list(n.b.all())
print(time.time() - start)

Output: 2.642864465713501

Таким образом, этот запрос занимает более 2 секунд только для 1000 объектов. Производительность ужасна. У меня более 1000000 объектов в производстве.

Я попытался увеличить производительность с помощью prefetch_related:

import time 
start = time.time()
objects = A.objects.all().prefetch_related('b')
for n in objects.iterator(): 
     list(n.b.all())
print(time.time() - start)

Output: 2.684298038482666

Но это ничего не помогает. Почему это так медленно и как я могу улучшить производительность?

Ответы [ 2 ]

1 голос
/ 13 марта 2019

При выполнении этого у меня около 0,7 сек с sqlite.Время сокращается на 50%, если я опускаю создание списка для каждого запроса.

Дело в том, что вы попадаете в БД столько раз, сколько у вас A объектов.Таким образом, лучшая ставка на повышение производительности - это уменьшение количества запросов, которые вы делаете.Но здесь действительно важно, что именно вы собираетесь делать.Как это неясно, с этого момента более или менее просто нужно угадать, что может сработать для вас ...

Может быть, просто перебрать A.objects.values_list('id') и запросить B вместо этого, поскольку вы на самом деле не используете a объектов:

bq = B.objects.all()
for a_id, in A.objects.values_list('id').iterator():
    list(bq.filter(b__id=a_id))  # maybe correct your related_name to 'a' so this would look a__id=a_id
print(time.time() - start)

или

start = time.time()
a_ids = A.objects.values_list('id').all()
bq = B.objects.all()
[list(bq.filter(b__id=a_id)) for a_id, in a_ids]
print(time.time() - start)

Если вы просто хотите, чтобы все b объекты были связаны с некоторыми из ваших a объектов, скажем, например, все с name_a содержит 'foo':

B.objects.filter(b__name_a__contains='foo').all().distinct()

Надеюсь, что эти предложения могут помочь

0 голосов
/ 13 марта 2019

Из документов Django для prefetch_related

Обратите внимание, что если вы используете iterator () для выполнения запроса, вызовы prefetch_related () будут игнорироваться, так как эти две оптимизации неиметь смысл вместе.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...