Django ORM 的选择性事务管理

3 投票
1 回答
1097 浏览
提问于 2025-04-17 10:32

我正在尝试使用Django的ORM在MySQL上设计一个批量数据导入的任务。通常,我会直接使用LOAD DATA INFILE命令,但我这次要导入的数据涉及到三个表,而且有些记录可能已经存在,所以我需要先检查这些记录是否已经存在,获取或创建它们的ID,然后在创建或获取其他记录时使用这个ID。

默认情况下,导入的速度是每秒0.8行,真是太慢了。我通过在受影响的表上运行DISABLE KEYS命令,把速度提高到了每秒1.5行,但考虑到我有几百万行数据,这依然太慢了。

有没有什么通用的建议,可以加快Django的ORM在批量导入复杂表关系时的速度呢?

我在考虑禁用Django的事务管理,把整个导入过程放在一个单一的事务中。不过,由于导入过程需要很长时间,它会定期更新一个状态模型,以报告完成的百分比。如果我把整个导入放在一个事务中,就无法更新这个状态记录。那么,有没有办法只对特定的模型禁用事务管理,同时仍然允许提交一个单独的模型呢?

我想做的事情是:

from django.db import transaction
transaction.enter_transaction_management()
transaction.managed(True)

from myapp.models import Status, Data

status = Status.objects.get(id=123)
try:
    data = magically_get_data_iter()
    for row in data:
        d,_ = Data.objects.get_or_create(**data.keys())
        d.update(data)
        d.save() # not actually visible in admin until the commit below
        if not row.i % 100:
            status.current_row = row.i
            status.total_rows = row.total
            # obviously doesn't work, but this should somehow actually commit
            status.save(commit=True)
finally:
    transaction.commit()

1 个回答

3

我解决这个问题的方法是把大量更新的模型和存储状态记录的模型放在不同的数据库里,然后把前一个数据库的事务管理功能关掉。

比如说,我上面例子的简化版:

django.db.transaction.enter_transaction_management(using='primary')
django.db.transaction.managed(True, using='primary')

i = 0 
for record in records:
    i += 1
    r = PrimaryDBModel(**record)
    r.save() # This will no be committed until the end.
    if not i % 100:
        SecondaryDBModel.update()
        status = SecondaryDBModel(id=123)
        status.current_row = i
        status.save() # This will committed immediately.

django.db.transaction.commit(using='primary')
django.db.transaction.leave_transaction_management(using='primary')

撰写回答