【问题标题】:Django - Cannot perform another query while using a queryset iterator()Django - 使用查询集迭代器()时无法执行另一个查询
【发布时间】:2019-09-06 14:38:56
【问题描述】:

我将 Django 1.11 与 MySQL 一起使用。在短期内升级到 2 是不可行的,因此不是我当前问题的可接受解决方案,但提到 Django 2 的答案可能会帮助其他人,所以请随时发布它们。

我需要对表中的所有行执行数据迁移。行数少于 40000,但它们非常大 - 其中两列是大约 15KB 的 JSON,在加载模型时会被解析。 (这些是我在数据迁移中需要使用的行,所以我不能defer他们)

为了不将所有对象同时加载到内存中,我想我会使用queryset.iterator,它一次只解析第 100 行。如果我所做的只是读取结果,这很好用,但是如果我执行另一个查询(例如到save 其中一个对象),那么一旦我到达当前 100 个结果块的末尾,下一个 100 个结果块是未获取,迭代器结束。

好像fetchmany 从中获取行的结果集已经丢失。

使用./manage.py shell来说明场景 (假设存在 40000 个具有顺序 id 的 MyModel)

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

上面按预期打印了 1 到 40000 的 id。

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

上面只打印 ids 1 到 100

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

上面只打印 ids 1 到 200

obj.save 替换为对数据库进行查询的任何其他内容(例如app.models.OtherModel.objects.first())具有相同的结果。

在使用 queryset 迭代器时是否根本不可能进行另一个查询?有没有其他方法可以达到同样的目的?

谢谢

【问题讨论】:

  • 使用paginator 并循环浏览所有页面怎么样?
  • 使用 Paginator 是一个潜在的解决方案,谢谢。我调查并在我的答案中添加了细节。但是我仍然想了解迭代器发生了什么。

标签: django django-1.11 django-mysql


【解决方案1】:

正如@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。

【讨论】:

    猜你喜欢
    • 2018-11-18
    • 2013-07-09
    • 1970-01-01
    • 2016-08-09
    • 2017-07-09
    • 2016-05-09
    • 1970-01-01
    • 2012-04-27
    • 1970-01-01
    相关资源
    最近更新 更多