Django事务使用多个数据库

3

我有三个数据库,希望在一个请求中以事务的方式向它们添加数据。

例如我的代码:

@transaction.commit_manually
def my_view(request):
    try:
        MyModel_one(...).save(using='default')
        MyModel_two(...).save(using='db_one')
        MyModel_three(...).save(using='db_two') # <-- for example we make exception here (duplicate data)
    except IntegrityError:
        transaction.rollback()
        transaction.rollback(using='db_one')
        transaction.rollback(using='db_two')
        return ...
    transaction.commit()
    transaction.commit(using='db_one')
    transaction.commit(using='db_two')
    return ...

结果:

  1. MyModel_one — 数据未保存
  2. MyModel_two — 数据已保存(我不知道为什么)
  3. MyModel_three — 数据未保存,因为出现了希望的错误

问题:为什么事务在这里没有起作用?我该怎么做才能让事务生效?

(我尝试使用其他方法,但没有成功的结果。 (同时,我明白这不是优美的代码,但它非常简单,可以理解问题)

Django 1.7.7 Python 2.7

数据库: default — postgres db_one — mysql db_two — mysql

1个回答

7
在Django 1.6中,transaction.commit_manually装饰器已过时。你应该转换为Django的新事务管理。
我认为你不需要手动提交事务,你可以嵌套transaction.atomic
def my_view(request):
    try:
        with transaction.atomic(using='default'):
            with transaction.atomic(using='db_one'):
                with transaction.atomic(using='db_two'):
                    MyModel_one(...).save(using='default')
                    MyModel_two(...).save(using='db_one')
                    MyModel_three(...).save(using='db_two') # raises exception
    except IntegrityError:
        return ...
    return ...

数据成功保存在数据库'db_one'的'MyModel_tow'表中。我不明白为什么它们被保存了。 - kalloc
我改变了我的答案,并将原子块移动到try except内部。你可以试试吗? - Alasdair
我找到了解决方案。问题在于表类型。MyISAM 不支持事务。 - kalloc
这个机制如何保证在3个数据库之间的事务一致性呢?提交顺序应该是db_two,db_one,default。事务不能在最外层块中一起提交,因为事务仅作用于特定的连接。如果向db_one提交失败,而db_two已经被提交,则事务无法完全回滚,这样会怎么样呢? - Yituo
1
@Yituo,我写这篇答案已经有一段时间了,但看起来如果异常是在内部原子块中引发的(例如在MyModel_three(...).save(using='db_two')这一行中,就像在原始问题中一样),它似乎会起作用。如果在将事务提交到db_one时引发异常,则如您所说,db_two已经提交。处理这个问题更难,我没有解决方案。 - Alasdair
我认为要处理分布式事务,我们需要使用2PC(两阶段提交)方法、3PC(三阶段提交)或SAGA。在这种情况下,我认为2PC就可以胜任。 - zabusa

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接