2015-01-03 4 views
1

Я новичок в Django и базах данных, поэтому я пытаюсь получить представление о производительности. В частности, я хочу понять, работает ли select_related() так, как я думаю.Django: Использование Select_related() и время выполнения исполнения

Вот упрощенная версия моей модели:

class User(models.Model): 
    short = models.CharField(max_length=255) 
    name = models.CharField(max_length=255) 

class Comment(models.Model): 
    title = models.CharField(max_length=255) 
    content = models.TextField() 
    short = models.ForeignKey(User) 

В моем шаблоне, я должен показать короткое имя пользователя рядом с заголовком комментария. Размер тестовой базы данных составляет 1000 пользователей и 19000 комментариев.

Во-первых, я был получение списка следующим образом:

cmt_list = Comment.objects.all().order_by('title') 

Мой шаблон был доступ к внешнему ключу short, который вызывает дополнительные обращения к базе данных. Получение всех полученных данных ~ 30s. Это было ужасно.

Я знал, что это намного быстрее - это сырой SQL, и я не мог понять, как это сделать с помощью Django ORM. Таким образом, я использовал интерфейс низкого уровня:

from django.db import connection 

cursor = connection.cursor() 
cursor.execute("SELECT app_comment.title,app_user.short  \ 
         FROM app_comment,app_user    \ 
         WHERE app_comment.short_id=app_user.id \ 
         ORDER BY app_comment.title" 
      ) 
raw_list = cursor.fetchall() 
cmt_list = [ {"title":entry[0], "short":entry[1]} for entry in raw_list] 

ВОССТАНОВЛЕНИЕ все данные взяли ~ 233ms. Это то, чего я ожидал!

После прочтения большего количества документов я обнаружил функцию select_related() в Django. Итак, я попробовал, что:

cmt_list = Comment.objects.all().select_related('short__short').order_by('title') 

Получение всех данных взял ~ 1.3s. Гораздо лучше, чем оригинал, но все же довольно медленный по сравнению с необработанными SQL-запросами.

Вопрос

Что я делаю не так в том, что я использую select_related (отдельно)/Django ORM? Я понимаю, что ORM добавит некоторые накладные расходы, но 1.3s против 233ms кажется чрезмерным. Или это ожидалось, и мне просто нужно преодолеть это?

Как я могу создать запрос с использованием ORM, который был бы эквивалентен необработанному SQL-запросу, который я сделал? Учитывая то, что я понимаю о select_related(), мой исходный SQL-запрос и мой Django должны быть примерно эквивалентными. (Запрос Django будет захватывать больше контента, но для моих тестовых данных их было бы не намного больше.)

+0

Вы можете посмотреть сгенерированный запрос, напечатав 'Comment.objects.select_related ('short'). Order_by ('title'). Query'. –

+0

Если вы хотите проверить, вызвана ли разница из-за дополнительных служебных данных (например, что Django извлекает «контент»), загляните в '.only()' или '.defer()'. –

+0

Ах, ха! Я не понимал, насколько легко было увидеть запрос, который делал Django. Это упростит мои тесты. Спасибо @KevinChristopherHenry. – hoadlck

ответ

0

На самом деле, ORM может вызвать довольно много накладных расходов. Следующий оператор производит одинаковые запросы (вы можете проверить это путем сравнения str(cmt_list.query)):

cmt_list1 = Comment.objects.all() 
cmt_list2 = Comment.objects.values('id', 'title', 'content', 'short_id') 

Однако простой тест с использованием timeit.timeit (с другой моделью в моем локальном проекте) показывает второй метод немного более чем в два раза так же быстро, как и первый метод.

Это, и не забывайте, что TextField - это огромное количество данных по сравнению с колонкой int или varchar(255). Я уверен, что если вы получите столбец content в своем сыром sql-запросе, цифры будут намного ближе.

0

Следуя рекомендациям в ответе, я обновил свой запрос с помощью Django ORM. Я должен был добавить небольшое понимание списка, чтобы дать тот же вклад в мой шаблон. Вот результат:

cmt_list = [ {"title":entry["title"], "short":entry["short__short"]} 
       for entry in 
       Comment.objects.order_by('title').values("title", 'short__short') 
       ] 

времени, чтобы сделать запрос был ~ 280ms, что намного больше, рядный с тем, что я могу получить от использования сырого SQL.

Чтобы определить, что исходный SQL был за кулисами в Django ORM, я использовал следующее заявление для печати ...

print(Comment.objects.order_by('title').values("title", 'short__short').query) 

Результаты были следующими:

SELECT "app_comment"."title", "app_user"."short" 
FROM "app_comment" INNER JOIN "app_user" 
ON ("app_comment"."short_id" = "app_user"."id") 
ORDER BY "app_comment"."title" ASC 

Что было так же, как и я.

Это решение не требовало select_related().

Смежные вопросы