Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/341.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 总账并发(django原子操作)_Python_Sql_Django_Concurrency - Fatal编程技术网

Python 总账并发(django原子操作)

Python 总账并发(django原子操作),python,sql,django,concurrency,Python,Sql,Django,Concurrency,我想知道如果有一个总账,如何处理并发性。考虑这样的模式: id | account_id | credit | debit | balance | 1 | 123 | 0 | 100 | 200 | 2 | 456 | 100 | 0 | 100 | 要向分类账添加新条目,我将执行以下操作(伪代码): 我认为这种方法的问题是: 假设有一个请求,我必须在分类账中输入新条目。新条目将增加帐户余额 如果在运行上面

我想知道如果有一个总账,如何处理并发性。考虑这样的模式:

id   | account_id | credit | debit | balance |
1    | 123        | 0      | 100   | 200     |
2    | 456        | 100    | 0     | 100     |
要向分类账添加新条目,我将执行以下操作(伪代码):

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

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

如果在运行上面的代码的过程中(例如,在获得最后一个条目之后)还有另一个请求将再次增加平衡。 因此,余额将增加一次,另一个请求将使用相同的余额保存一个新条目,因为它将只使用以下内容:

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

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

假设您的数据库支持它(为此,它应该),将整个操作包装在一个事务中。即以“启动事务”调用开始,以提交结束

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


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

假设您的数据库支持它(为此,它应该),则将整个操作包装在一个事务中。即以“启动事务”调用开始,以提交结束

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


具体执行什么以及如何执行通常取决于数据库,因为事务处理与行和表锁定之间的关系因数据库和引擎而异。

计算将应用于
余额的总差异
,并使用
更新
查询:

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

计算将应用于
余额的总差额
,并使用
更新
查询:

Model.objects.filter(pk=entry.pk).update(balance=F('balance') + difference)
您可以使用(返回将锁定行直到事务结束的查询集):

您可以使用(返回将锁定行直到事务结束的查询集):


你能看看我的密码吗?我在问题中添加了一些示例代码。你觉得有什么问题吗?谢谢。我现在明白了。因此,一个打开的事务所做的更改对其他正在运行的事务可见。我更新了我的代码示例。现在简单多了。请注意,要使
select\u for\u update
起作用,您需要在事务内部。@kroolik因为django 1.6是默认行为,所以您总是在事务内部,在此处检查有关自动提交的文档您不希望在提交时自动提交
选择\u进行\u更新
。可以查看我的代码吗?我在问题中添加了一些示例代码。你觉得有什么问题吗?谢谢。我现在明白了。因此,一个打开的事务所做的更改对其他正在运行的事务可见。我更新了我的代码示例。现在简单多了。请注意,要使
select\u for\u update
工作,您需要在事务内部。@kroolik因为django 1.6是默认行为,您总是在事务内部,请在此处检查有关自动提交的文档,您不希望在提交时为
select\u for\u 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()
last_entry.update(balance=F('balance')+amount)