1

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

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

为了不将所有对象同时加载到内存中,我想我会使用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()相同的结果。

使用查询集迭代器时根本不可能进行另一个查询吗?有没有另一种方法来实现同样的目标?

谢谢

4

1 回答 1

0

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

于 2019-04-16T14:22:45.393 回答