Django - Невозможно выполнить другой запрос при использовании итератора набора запросов () - PullRequest
1 голос
/ 16 апреля 2019

Я использую Django 1.11 с MySQL.Обновление до 2 не представляется возможным в краткосрочной перспективе, поэтому не является приемлемым решением моей непосредственной проблемы, но ответы на Django 2 могут помочь другим, поэтому не стесняйтесь их публиковать.

Мне нужно выполнитьмиграция данных по всем строкам в таблице.В нем менее 40000 строк, но они довольно большие - два столбца имеют размер ~ 15 КБ JSON, который анализируется при загрузке модели.(Это строки, которые мне нужно использовать при переносе данных, поэтому я не могу отложить их)

Чтобы не загружать все объекты в память одновременно, я решил использовать queryset.iterator , который одновременно анализирует только строки 100.Это прекрасно работает, если все, что я делаю, это читаю результаты, но если я выполняю другой запрос (например, к save одному из объектов), то, как только я достигну конца текущего фрагмента из 100 результатов, следующий блок из 100 результатов будетне извлекается, и итератор завершается.

Это как если бы набор результатов, который fetchmany извлекает из строк, был потерян.

Чтобы проиллюстрировать сценарий, используя ./manage.py shell (Предположим, что существует 40000 MyModel с последовательными идентификаторами)

iterator = app.models.MyModel.objects.iterator()
for obj in iterator:
  print(obj.id)

Приведенный выше выводит на печать идентификаторы от 1 до 40000.

iterator = app.models.MyModel.objects.iterator()
for obj in iterator:
  print(obj.id)
  obj.save()

Приведенный выше выводит на печать только идентификаторы от 1 до 100

iterator = app.models.MyModel.objects.iterator()
for obj in iterator:
  print(obj.id)
  if obj.id == 101:
    obj.save()

Приведенный выше выводит только идентификаторы с 1 по 200

Замена obj.save на что-либо еще, что делает запрос к БД (например, app.models.OtherModel.objects.first()), имеет тот же результат.

Разве просто невозможно сделать другой запрос при использовании итератора набора запросов?Есть ли другой способ добиться того же?

Спасибо

1 Ответ

0 голосов
/ 16 апреля 2019

Как предлагает @dirkgroten, Paginator - это альтернатива итератору, которая потенциально является лучшим решением с точки зрения использования памяти, поскольку использует секцию в наборе запросов, которая добавляет предложения OFFSET и LIMIT для получения только части полного набора результатов.

Однако высокие значения OFFSET влекут за собой снижение производительности MySQL: https://www.eversql.com/faster-pagination-in-mysql-why-order-by-with-limit-and-offset-is-slow/

Поэтому поиск по индексированному столбцу может быть лучшим вариантом:

chunk_size = 100
seek_id = 0
next_seek_id = -1
while seek_id != next_seek_id:
  seek_id = next_seek_id
  for obj in app.models.MyModel.objects.filter(id__gt=seek_id)[:chunk_size]:
    next_seek_id = obj.id
    # do your thing

Кроме того, если ваши данные таковы, что выполнение запроса стоит недорого, а создание экземпляров модели - это, итератор имеет потенциальное преимущество при выполнении одного запроса к базе данных. Надеемся, что другие ответы смогут пролить свет на использование queryset.iterator с другими запросами.

...