如何在南方数据迁移中执行手动提交?
我创建了一个长期运行的南方数据迁移。默认情况下,South似乎将整个迁移包装在单个事务中。但是,由于我正在修改数百万行,如果我尝试在单个事务中存储所有挂起的更改,这将导致数据库占用我的所有系统内存。
为了防止这种情况,我试图在每N条记录中调用Django的commit()
,但这会抛出异常:
TransactionManagementError: This code isn't under transaction management
即使我使用forwards()
包裹迁移的@commit_on_success
方法或在[commit_manually()][1]
的开头调用forwards
,我仍然会在调用{{1}时遇到异常或南方自己的commit()
。
我该如何解决这个问题?
编辑:如果我用db.commit_transaction()
包裹forwards()
,这会修复错误,但是,@commit_manually
似乎仍然没有实际提交更改。
我的代码如下:
commit()
我的总数约为300万。当它正在运行时,我将从SQL终端class Migration(DataMigration):
@commit_manually
def forwards(self, orm):
mdl = orm['myapp.MyModel']
q = mdl.filter(criteria=value_to_change).only('id')
total = q.count()
i = 0
pending = []
tmp_debug = settings.DEBUG
settings.DEBUG = False
try:
for record in q.iterator():
i += 1
if i == 1 or not i % 10 or i == total:
print '\rProcessing %i of %i.' % (i, total),
sys.stdout.flush()
mdl.objects.filter(id__in=pending).update(criteria=new_value)
pending = []
# These don't seem to do anything, as the changes above aren't visible.
#commit()
db.commit_transaction()
pending.append(record.id)
finally:
settings.DEBUG = tmp_debug
进行查询,并且显示的计数永远不会改变。
但是,如果我改变上面的代码来执行select count(*) from myapp_mymodel where criteria=value_to_change
所以它几乎立即完成,我的SQL查询会在迁移完成后显示更改,这意味着我的q = q[:100]
什么都不做,而且commit()
是真的{{ 1}}仅在commit()
完成后运行。造成这种情况的原因是什么?
答案 0 :(得分:1)
您可以按块检索行,手动启动事务并在每个已处理的块之后提交事务。
这里描述的主要想法(经过测试!):
def queryset_iterator(queryset, chunksize=1000):
pk = 0
last_pk = queryset.order_by('-pk')[0].pk
queryset = queryset.order_by('pk')
while pk < last_pk:
db.commit_transaction() # Commit the first transaction
db.start_transaction() # Start the second, committed on completion
for row in queryset.filter(pk__gt=pk)[:chunksize]:
pk = row.pk
yield row
qs = orm['myapp.MyModel'].filter(criteria=value_to_change).only('id')
for row in queryset_iterator(qs):
row.criteria=new_value
row.save()
希望这些帮助!