Оптимизация запросов Django Rest ORM - PullRequest
0 голосов
/ 24 марта 2019

Я реактивный интерфейс, django backend (используется как REST back).Я унаследовал приложение, и оно загружает все пользовательские данные, используя множество моделей и сериализаций.Он загружается очень медленно.Он использует фильтр для запроса отдельного члена, а затем передает его в сериализатор:

found_account = Accounts.objects.get(id='customer_id')
AccountDetailsSerializer(member, context={'request': request}).data

Тогда существует так много различных вложенных сериализаторов:

AccountDetailsSerializers(serializers.ModelSerializer):
   Invoices = InvoiceSerializer(many=True)
   Orders = OrderSerializer(many=True)
   ....

Из просмотра журналаПохоже, что ORM выдает так много запросов, это безумие, для некоторых конечных точек у нас получается 50-60 запросов.

  1. Должен ли я попытаться использовать select_related и prefetch илиВы пропускаете все это и просто пытаетесь написать один SQL-запрос, чтобы сделать несколько объединений и получить все данные сразу как json?

  2. Как я могу определить prefetch / select_related, когда я передаюв одном объекте (результат get), а не в набор запросов к сериализатору?

  3. Некоторые сущности БД не имеют связей между ними, то есть не fk или многие другие отношения, просто удерживайтеполе, у которого есть идентификатор для другого, но связь в базе данных не применяется?Будет ли это проблемой для меня?Означает ли это еще раз, что я должен пропустить подход select_related и написать клиентский sql для выборки?

  4. Как бы вы посоветовали подойти к настройке производительности в этом кошмаре запросов?

Ответы [ 2 ]

3 голосов
/ 24 марта 2019

Я рекомендую сначала посмотреть, какие эффекты вы получаете с prefetch_related.Это может оказать существенное влияние на время загрузки, и его реализация довольно тривиальна.Если исходить из приведенного выше примера, то подобное может значительно сократить время загрузки:

AccountDetailsSerializers(serializers.ModelSerializer):

    class Meta:
        model = AccountDetails
        fields = (
            'invoices',
            'orders',
        )

    invoices = serializers.SerializerMethodField()
    orders = serializers.SerializerMethodField()

    def get_invoices(self, obj):
        qs = obj.invoices.all()\
            .prefetch_related('invoice_sub_object_1')\
            .prefetch_related('invoice_sub_object_2')
        return InvoiceSerializer(qs, many=True, read_only=True).data

    def get_orders(self, obj):
        qs = obj.orders.all()\
            .prefetch_related('orders_sub_object_1')\
            .prefetch_related('orders_sub_object_2')
        return OrderSerializer(qs, many=True, read_only=True).data 

Что касается вашего вопроса архитектуры, я думаю, что многие другие факторы влияют на то, следует ли и в какой степени выполнять рефакторинг.кодовая база.В целом, если вы состоите в браке с Django и DRF, у вас будет больше возможностей для разработчиков, если вы сможете использовать идиомы и шаблоны этих фреймворков вместо того, чтобы пытаться купить их со своими собственными исправлениями.

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

Нет серебряной пули, если не смотреть детально на код (и результаты профилирования).

Единственное, что не составляет никакого труда, - это навязывание отношений в моделях и в базе данных.Это предотвращает целый ряд ошибок, поощряет использование стандартизированного, производительного доступа (вместо того, чтобы сочинять SQL на месте, который чаще всего будет содержать ошибки и медленно), и делает ваш код и короче, игораздо более удобочитаемым.

Кроме этого, 50-60 запросов может быть много (если вы могли бы сделать ту же работу с одним или двумя), или это может быть просто правильно - это зависит от того, чего вы достигнете сих.

Использование prefetch_related и select_related важно, да - но только при правильном использовании;в противном случае это может замедлить вас, а не ускорить.

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

Время основных частей медленных представлений, проверить отправленные запросы SQL и проверить, действительно ли вам нужны все возвращаемые данные.

Затем вы можете посмотреть на больные места и получить время, когда оновопросы.Задавая конкретные вопросы о SO с помощью полных примеров кода, вы также сможете быстро справиться.


Если у вас есть только один объект верхнего уровня, вы можете усовершенствовать подход, предлагаемый @jensmtg, выполнив все предварительные выборки, которыевам нужно на этом уровне, а затем для более низких уровней просто использовать ModelSerializer с (не SerializerMethodField с), которые получают доступ к предварительно выбранным объектам.Посмотрите на объект Prefetch , который допускает вложенную предварительную выборку.

Но имейте в виду, что prefetch_related не бесплатна, она требует некоторой обработки в Python;вам может быть лучше использовать плоские (как в db-view) соединенные запросы с values() и values_list.

...