#python #mysql #django #database #memory-management
Вопрос:
У меня довольно большая таблица базы данных (1 м строк) , которая испытывает проблемы при фильтрации результатов в Django.
В настоящее время логика фильтра выглядит следующим образом:
results = Result.objects.filter(date__range=(date_from, date_to))
for result in results:
# do stuff
В некоторые периоды это вызывает сбой (вероятно, из-за истощения памяти).
Мне интересно, было бы более эффективно заменить его следующим:
results = Result.objects.all().order_by('-id')
for result in results:
if result.date > date_to:
continue
if result.date < date_from:
break
# do stuff
Теоретически, поскольку .all() создает ленивый набор запросов, он может работать лучше, чем фильтрация диапазонов в базе данных с 1M строками.
Также было бы интересно узнать о потреблении памяти в обоих случаях.
Может быть, есть другое решение, как сделать это более эффективно и в постоянной памяти?
Спасибо!
Ответ №1:
Теоретически, поскольку
.all()
создается ленивый набор запросов, он может работать лучше, чем фильтрация диапазонов в базе данных с 1M строками. Также было бы интересно узнать о потреблении памяти в обоих случаях.
Набор запросов ленив в том смысле, что он будет извлекать объекты только в случае необходимости, но как только ему придется это сделать, он получит все элементы. .all()
Кстати, ленив не только ленив, все QuerySet
s ленивы, поэтому, если вы определяете Result.objects.filter(…)
набор запросов, это QuerySet
тоже лениво.
Но независимо от того, как это реализовано, фильтрация на стороне базы данных более эффективна, поскольку базы данных предназначены для этого, и это приводит к меньшей пропускной способности от базы данных до уровня Python/Django.
В случае возникновения проблем с памятью это, скорее всего , означает, что ваш QuerySet
файл, даже если он отфильтрован, слишком велик для хранения в памяти. Вы можете работать с .iterator(…)
методом [Django-doc] для загрузки пакетов элементов, которые затем могут быть обработаны:
results = Result.objects.filter(date__range=(date_from, date_to)).iterator()
for result in results:
# …
pass
Если будет каждый раз загружать в память фрагмент записей, которые затем могут быть обработаны. Если вы не сохраняете элементы (например, в списке), Python может повторно использовать память для следующего фрагмента.
Комментарии:
1. Я бы упомянул невысказанный факт, который
.filter()
также дает набор запросов и так же ленив, как.all()
и есть 🙂2. @WillemVanOnsem Отлично! Я думаю, это то, что я хотел получить — наличие итератора, который будет выполнять выборку с помощью фрагментов. Будет реализован и сообщит вам, если это решит проблему. Спасибо за быстрый ответ!
3. @WillemVanOnsem, с которым я раньше не был знаком
.iterator()
. Итак, это означало, что это действительно то, что я искал 😊 Теперь нужно попробовать это в среде с этим большим набором данных4. @WillemVanOnsem
iterator()
определенно улучшил ситуацию! Однако, когда проходит несколько более длительных периодов — его все равно убивают… Вероятно, нужно придумать что-то еще.5. @EgorWexler: если это possbile, вы можете захотеть сделать меньшие «куски» диапазонов даты и времени и, таким образом, работать с разными запросами.