Рассматриваемая таблица содержит примерно десять миллионов строк.
for event in Event.objects.all():
print event
Это приводит к неуклонному увеличению использования памяти до 4 ГБ или около того, после чего строки печатаются быстро. Длительная задержка перед печатью первой строки меня удивила - я ожидал, что она распечатается почти мгновенно.
Я также пробовал, Event.objects.iterator()
который вел себя так же.
Я не понимаю, что Django загружает в память и почему он это делает. Я ожидал, что Django будет перебирать результаты на уровне базы данных, что означало бы, что результаты будут выводиться примерно с постоянной скоростью (а не все сразу после длительного ожидания).
Что я неправильно понял?
(Не знаю, актуально ли это, но я использую PostgreSQL.)
sql
django
postgresql
django-orm
davidchambers
источник
источник
Ответы:
Нейт Си был близок, но не совсем.
Из документов :
Таким образом, ваши десять миллионов строк извлекаются сразу, когда вы впервые входите в этот цикл и получаете повторяющуюся форму набора запросов. Ожидание, которое вы испытываете, - это то, что Django загружает строки базы данных и создает объекты для каждой, прежде чем вернуть что-то, что вы действительно можете перебрать. Тогда у вас есть все в памяти, и результаты выливаются наружу.
Из того, что я читал в документации,
iterator()
ничего не делает, кроме как обойти внутренние механизмы кэширования QuerySet. Я думаю, что было бы разумно делать это по отдельности, но для этого, наоборот, потребовалось бы десять миллионов отдельных обращений к вашей базе данных. Может быть, не все так желательно.Эффективный перебор больших наборов данных - это то, что мы до сих пор не совсем поняли, но есть некоторые фрагменты, которые могут оказаться полезными для ваших целей:
источник
Может быть, не самым быстрым или эффективным, но в качестве готового решения почему бы не использовать объекты Paginator и Page из ядра django, описанные здесь:
https://docs.djangoproject.com/en/dev/topics/pagination/
Что-то вроде этого:
источник
Paginator
теперь имеетpage_range
свойство избегать шаблонов. Если вы ищете минимальные накладные расходы на память, вы можете использовать,object_list.iterator()
который не будет заполнять кеш набора запросов .prefetch_related_objects
затем требуется для предварительной выборкиПо умолчанию Django кэширует весь результат QuerySet при оценке запроса. Вы можете использовать метод итератора QuerySet, чтобы избежать этого кеширования:
https://docs.djangoproject.com/en/dev/ref/models/querysets/#iterator
Метод iterator () оценивает набор запросов, а затем считывает результаты напрямую, не выполняя кэширование на уровне QuerySet. Этот метод приводит к повышению производительности и значительному сокращению памяти при итерации большого количества объектов, к которым вам нужно получить доступ только один раз. Обратите внимание, что кеширование по-прежнему выполняется на уровне базы данных.
Использование iterator () уменьшает использование памяти для меня, но все равно выше, чем я ожидал. Использование подхода paginator, предложенного mpaf, использует гораздо меньше памяти, но в моем тестовом примере это в 2-3 раза медленнее.
источник
Это из документов: http://docs.djangoproject.com/en/dev/ref/models/querysets/
Поэтому при
print event
запуске запроса запускается (что является полным сканированием таблицы в соответствии с вашей командой) и загружаются результаты. Вы просите все объекты, и нет возможности получить первый объект, не получив их всех.Но если вы сделаете что-то вроде:
http://docs.djangoproject.com/en/dev/topics/db/queries/#limiting-querysets
Затем он добавит смещения и ограничения в sql внутри.
источник
Для большого количества записей курсор базы данных работает еще лучше. Вам нужен необработанный SQL в Django, Django-cursor - это нечто иное, чем SQL-курсор.
Метод LIMIT - OFFSET, предложенный Nate C, может быть достаточно хорош для вашей ситуации. Для больших объемов данных он работает медленнее, чем курсор, потому что ему приходится выполнять один и тот же запрос снова и снова и переходить через все больше и больше результатов.
источник
У Django нет хорошего решения для извлечения больших элементов из базы данных.
values_list можно использовать для получения всех идентификаторов в базах данных, а затем для получения каждого объекта отдельно. Со временем в памяти будут создаваться большие объекты, которые не будут собираться сборщиком мусора, пока цикл for не будет завершен. Приведенный выше код выполняет ручную сборку мусора после использования каждого 100-го элемента.
источник
Потому что таким образом объекты для всего набора запросов загружаются в память сразу. Вам нужно разбить ваш запрос на более мелкие удобоваримые части. Схема для этого называется кормлением с ложечки. Вот краткая реализация.
Чтобы использовать это, вы пишете функцию, которая выполняет операции с вашим объектом:
а затем запустите эту функцию в своем запросе:
Это можно улучшить с помощью многопроцессорной обработки для
func
параллельного выполнения нескольких объектов.источник
Вот решение, включающее len и count:
Использование:
источник
Я обычно использую необработанный исходный запрос MySQL вместо Django ORM для такого рода задач.
MySQL поддерживает режим потоковой передачи, поэтому мы можем быстро и безопасно перебирать все записи без ошибок нехватки памяти.
Ref:
источник
queryset.query
для своего выполнения.