【问题标题】:Optimistic "locking" with Django transactionsDjango 事务的乐观“锁定”
【发布时间】:2017-06-07 11:56:04
【问题描述】:

我有一个函数fn() 需要以原子方式执行一些数据库工作,这些工作依赖于某些数据集在其执行期间不会发生变化(大多数情况下都是如此)。

在 Django 中实现这一点的正确方法是什么?基本上我想做这样的事情:

def ensure_fn_runs_successfully():
  # While commit unsuccessful, keep trying
  while not fn():
    pass

@transaction.atomic
def fn():
  data = read_data_that_must_not_change()

  ... do some operations with the data and perform database operations ...

  # Assume it returns true if commit was successful, otherwise false
  return commit_only_if_the_data_actually_didnt_change()

@transaction.atomic 解决了部分问题(数据库应该只看到fn 运行之前或fn 成功运行之后的状态),但我不确定是否存在一个好的原语来做commit_only_if_the_data_actually_didnt_change,如果失败则重试操作。

要验证数据没有改变,只需检查查询返回的项目数是否与函数开始时相同即可;但是,我不知道是否有任何原语可以让您同时做出检查和提交决定/没有竞争条件。

【问题讨论】:

  • 提供一个最小、完整和可验证的示例将真正帮助我们为您的问题提供完整的答案。

标签: python django locking atomic


【解决方案1】:

如果您在一个事务块中,唯一可以改变您正在读取的数据的是同一事务块中的其他操作。因此,只要fn() 不对data 进行任何更改,就可以保证数据不会更改,除非fn() 更改它。这就是交易要解决的问题。

如果data 可以在fn() 的范围内发生变化,只需跟踪它发生变化的地方或跟踪最终结果。

@transaction.atomic
def fn():
  data = read_data_that_must_not_change()
  original_data = copy.copy(data)
  ... do some operations with the data and perform database operations ...

  # Assume it returns true if commit was successful, otherwise false
  if data != original_data:
    raise Exception('Oh no!  Data changed!') 
    # raising in exception is how you prevent transaction.atomic
    # from committing
  return commit_only_if_the_data_actually_didnt_change()

然后像这样处理你的while循环中的异常:

while True:
    try:
        fn()
        break
    except:
        time.sleep(10) # ten second cool off
        pass

【讨论】:

  • 是的,它会起作用的。如果您担心对item_set 的更改是数据库中的更改,那么事务会使事务之外对item_set 的任何更改无法同时执行。对 item_set 的更改将完全在事务块之前或完全在事务块之后发生。酸等等。
  • 这里有一本关于 ACID 和交易的很好的入门书,因为这似乎是你所缺少的:technet.microsoft.com/en-us/library/ms190612(v=sql.105).aspx
  • 我认为你的这种说法只能适用于serializable事务,但澄清数据可以更改,只是我们的事务可能会被数据库引擎失败:>如果您在一个事务块中,唯一可以更改您正在读取的数据的是同一事务块中的其他操作。在较低级别的隔离中(例如,read commited)我们的事务块可能会遇到 nonrepeatable readsphantom reads,因为来自外部的更改它:postgresql.org/docs/9.5/transaction-iso.html
  • 另外值得注意的是,出于性能原因,默认情况下几乎没有数据库执行可序列化隔离级别。例如。 Django 对 PostgreSQL 的默认设置是读取提交。
猜你喜欢
  • 1970-01-01
  • 2010-09-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-10-22
  • 1970-01-01
  • 1970-01-01
  • 2016-07-18
相关资源
最近更新 更多