Что заставляет DRF Viewsets выполнять неэффективные SQL-запросы? - PullRequest
0 голосов
/ 06 июня 2019

Окружающая среда:

Django=="2.2"
Python=="3.6.8"
debug_toolbar=="1.11"
rest_framework=="3.8.2"

Фон:

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

Контекст модели псевдокода:

Creator:
  ForeignKey.Shipments[]

Unit:
  ForeignKey.Shipments[]

Shipment:
  ForeignKey.Addresses[]
  ForeignKey.Creator
  ForeignKey.Unit

Address:
  Street
  City
  State

Интересный контекст сериализатора:

Unit:
  Meta:
    Depth = 1

Creator:
  Meta:
    Depth = Infinite

При сравнении Viewsets вышеуказанной структуры с эквивалентным @apiview, я нахожу:

  • /addresses работает немного лучше на счетчике запросов SQL (0,9x), с нагрузкой на процессор в 1,1x (ожидается)
  • /creators работает немного хуже на SQL-запросе (1.1x), с 3.9x нагрузкой на процессор (ожидается)
  • /creators/1 с набором данных только для чтения работает немного лучше на счетчике запросов SQL (0,9x), с нагрузкой на процессор 3,4x (неожиданно)
  • /creators/1 с записываемым набором данных значительно хуже работает с SQL-запросом (3,3x), со значительными 5,9x нагрузками на ЦП (неожиданно)
  • /units/1 с записываемым представлением работает значительно хуже на SQL-запросе (4.4x), с огромными 7.9x нагрузками на процессор (ОЧЕНЬ неожиданно)
  • Обе формы представлений не выигрывают от / игнорируют prefetch_related использования от get_queryset полностью (ОЧЕНЬ неожиданно)

Разбивка извлечения против @apiview:

(240 queries including 235 similar and 120 duplicates)

Разбивка извлечения против viewsets.ReadOnlyModelViewSet:

(208 queries including 200 similar and 78 duplicates )

Разбивка извлечения против viewsets.ModelViewSet:

(803 queries including 801 similar and 801 duplicates )

Все это кажется очень не интуитивным.

Как возможно, чтобы два типа конечных точек, использующих один и тот же сериализатор, работали так по-разному?

По сравнению с представлениями, основанными на функциях, кажется, что классовые представления и наборы набирают массивную производительность ЦП и SQL, где задействованы внешние ключи, и я не могу найти никакой реальной причины.

Я ожидаю, что N + 1 запросы будут работать плохо, но я не ожидаю, что они будут выполнять больше плохо в зависимости от того, какой стиль конечной точки DRF обслуживает их.

Есть ли объяснение, почему это происходит?

Ответы [ 2 ]

0 голосов
/ 06 июня 2019

Статистика здесь неверна, это происходит потому, что просматриваемый API DRF выполняет дополнительный запрос для каждого метода, который принимает конечная точка.

Соответственно, если бы вы выполняли только GET @apiview при 1-кратной ссылкеspeed, конечная точка с GET, PUT, PATCH, DELETE, по-видимому, будет работать в 4 раза медленнее из-за выполнения 3 дополнительных запросов.

API-интерфейс для просмотра в DRF скрывает истинную скорость / количество запросов, когда на методе доступно более одного метода запросаконечная точка, и для того, чтобы увидеть реальную производительность конечной точки, вы должны запросить ее программно или использовать ?format=json.

Я обнаружил это, когда понял, что через профилирование класс разрешений был поражен 6 раз:

https://stackoverflow.com/a/52731612/784831

0 голосов
/ 06 июня 2019

Взгляните на этот ответ Оптимизация запросов к базе данных в Django REST framework .DRf не оптимизирует ваш запрос автоматически, вам все равно нужно сделать это самостоятельно.

И если вы хотите немного повысить производительность, взгляните на это приложение https://github.com/K0Te/drf-serializer-cache

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