我想知道在给定总帐的情况下如何处理并发。考虑这样的模式:

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


要将新条目添加到分类帐,我会做(伪代码):

last_entry = get last account entry
is_credit = figure out if it is debit or credit entry
is_liability = figure out type of account

new_entry = Entry(
    foo='bar'
    # etc
)

if is_liability and is_credit
    new_entry.balance = last_entry.balance + amount

if is_liability and !is_credit
    new_entry.balance = last_entry.balance - amount

if !is_liability and is_credit
    new_entry.balance = last_entry.balance - amount

if !is_liability and !is_credit
    new_entry.balance = last_entry.balance + amount

new_entry.save()


我看到的这种方法的问题是:

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

如果在运行上述代码的过程中(假设在获得最后一个条目之后)又有另一个请求会再次增加余额,该怎么办?

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

new_balance = last_entry.balance + amount


但是last_entry已被另一个请求过时,因此现在余额更高。

关于如何确保不会发生这种情况的任何想法(我知道这种可能性很小)。

更新:

遵循一些答案,我使用SELECT FOR UPDATE提出了这个解决方案:

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


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

最佳答案

您可以使用select_for_update(返回一个查询集,该查询集将锁定行直到事务结束):

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()


F表达式:


  F()对象表示模型字段的值。它做到了
  可以引用模型字段值并执行数据库
  使用它们的操作,而不必实际将它们拉出
  数据库存入Python内存。


例如:

last_entry.update(balance=F('balance')+amount)

10-07 19:33
查看更多