总账并发(Django原子操作)

2024-06-01 12:50:24 发布

您现在位置:Python中文网/ 问答频道 /正文

我想知道在一个总分类账下如何处理并发。考虑如下模式:

id   | account_id | credit | debit | balance |
1    | 123        | 0      | 100   | 200     |
2    | 456        | 100    | 0     | 100     |

要在分类帐中添加一个新条目,我将执行以下操作(伪代码):

^{pr2}$

我认为这种方法的问题是:

假设有一个请求来了,我必须在分类帐中输入新的分录。新的条目将增加帐户余额。在

如果在运行上述代码的过程中(比如在获得最后一个条目之后),有另一个请求将再次增加余额,那会怎样呢。在

因此余额将增加一次,另一个请求将用相同的余额保存一个新条目,因为它只会使用如下内容:

new_balance = last_entry.balance + amount

但最后一个_条目已被另一个请求过期,因此余额现在更高。在

有什么办法确保这种情况不会发生(我知道这不太可能)。在

更新:

根据一些答案,我使用SELECT FOR UPDATE想出了这个解决方案:

    with transaction.atomic():
        new_entries = prepare_entries()
        for new_entry in new_entries:
            new_entry.save()

这是解决潜在并发问题的好方法吗?在


Tags: 方法代码idnew模式条目account余额
3条回答

计算将应用于balance的总差异并使用update查询:

Model.objects.filter(pk=entry.pk).update(balance=F('balance') + difference)

{a1将返回一个事务^的锁,直到^结束为止:

with transaction.atomic(): # or commit_on_success/commit_manually in django < 1.6
    new_entries = prepare_entries()
    new_entries.select_for_update() # lock to update only in current transaction
    for new_entry in new_entries:
        #change new_entry somehow
        new_entry.save()

^{}表达式:

An F() object represents the value of a model field. It makes it possible to refer to model field values and perform database operations using them without actually having to pull them out of the database into Python memory.

例如:

^{pr2}$

假设您的数据库支持它(为此,它应该支持),将整个操作包装在事务中。一、 e以“start transaction”调用开始,以commit结束。在

这可以保证执行整个事务,或者不执行任何事务。执行此操作时,您可能还需要锁定表,以确保其他进程的外观一致。在

确切地说,您做什么以及如何做通常取决于数据库,因为事务处理和行与表锁定之间的关系因数据库和引擎而异。在

相关问题 更多 >