"transaction.atomic"和"transaction.commit_on_success"是相同的吗?

66

Django 1.6提出了@transaction.atomic,作为从1.5开始重新整理的事务管理的一部分。

我有一个函数,由Django管理命令调用,该命令又由cron调用,即在此情况下没有HTTP请求触发事务。代码片段:

from django.db import transaction

@transaction.commit_on_success
def my_function():
    # code here
在上面的代码块中,commit_on_success使用单个事务来完成my_function中的所有工作。

@transaction.commit_on_success替换为@transaction.atomic是否会产生相同的行为?@transaction.atomic 文档说明

原子性是数据库事务的定义属性。原子操作允许我们创建一个代码块,在其中保证了数据库的原子性。如果成功完成代码块,则将更改提交到数据库。如果出现异常,则更改将回滚。

我认为它们产生相同的行为;是这样吗?


4
为了更详细地阐述这一点,这里有一个非常好的Djangocon演讲关于这个问题... - Bernhard Vallant
2个回答

62

根据我所阅读的相关文档,当这些修饰符被嵌套时,会有显著区别。

嵌套两个atomic块与嵌套两个commit_on_success块的行为不同。

问题在于,您希望从这些块中获得两种保证。

  • 您希望块内的内容是原子性的,即块内的所有东西都被提交或未被提交。
  • 您希望具有耐久性,一旦您离开块且没有异常,您保证块内编写的所有内容都是持久的。

当块被嵌套时,不可能提供这两种保证。如果在离开最内层块但在离开最外层块之前引发异常,则必须以以下两种方式之一失败:

  • 无法为最内层块提供耐久性。
  • 无法为最外层块提供原子性。

这就是区别所在。使用commit_on_success将为最内层块提供耐久性,但为最外层块提供原子性。使用atomic将为最外层块提供原子性,但不为最内层块提供耐久性。

在嵌套情况下,简单地引发异常可以防止您遇到问题。最内层块始终会引发异常,因此它永远不会承诺任何耐久性。但是这会失去一些灵活性。

更好的解决方案是要有关于您所要求的东西的更多细节。如果您可以单独请求原子性和耐久性,则可以执行嵌套。您只需确保每个请求耐久性的块位于请求原子性的块之外。在请求原子性的块中请求耐久性必须引发异常。

atomic应该提供原子性部分。据我所知,django 1.6.1没有一个修饰符,可以要求耐久性。我尝试编写了一个,并将其发布在codereview上。


感谢@kasperd,除了第一个答案提供的内容(我已标记为正确),还提供了额外的细节。+1赞同。 - Joseph Victor Zammit
1
这是一个有趣的分析,但你所谓的耐久性并不是SQL数据库提供的保证。commit_on_success按照你描述的方式工作是一个错误(参见票号2227,8年前开放,只有在引入基于保存点的事务系统后才得以修复)。 - Kevin Christopher Henry
5
不同的数据库实现嵌套事务的方式不同。但是它们有一点相同,即在最外层事务提交之前,对数据的更改对任何不相关的事务都不可见。这意味着内部事务的提交并不一定会将更新持久化到数据库中。 - Kevin Christopher Henry
@KevinChristopherHenry 持久性是数据库事务首先要满足的基本要求。如果事务不能保证持久性,那么这个数据库基本上是无用的。你在暗示django在数据库本身中使用了嵌套事务,这对我来说听起来非常可疑。据我所知,postgres没有支持嵌套事务,如果django试图这样做,我会认为这是一个错误。正如我指出的,你需要分离不同的保证才能进行嵌套。 - kasperd
2
你混淆了事务。如果你将事务定义为具有原子性和持久性(在你的意义上),那么嵌套事务是不存在的,Django也无法支持它们。但是你的回答和我的评论都是关于嵌套的。Django支持这些块;它通过使用所有支持的SQL数据库中存在的标准保存点功能来实现;而这些嵌套块没有任何持久性保证。它们是否被持久化到数据库取决于它们所包含的事务的命运。 - Kevin Christopher Henry
@KevinChristopherHenry 不,我没有混淆块和事务。你是提到了事务,但这个词甚至不在我的回答中。任何明智的事务定义都会暗示原子性和持久性。是的,这样的结构不能嵌套,这也在我的回答中解释了。commit_on_success为应用于代码块的持久性提供了保证,但不提供原子性。atomic如其名称所示提供了原子性,但不提供持久性。显然,现在已经没有办法指定一个代码块需要持久性。 - kasperd

49

是的。应该在以前使用commit_on_success的地方使用atomic

由于新事务系统设计更为稳健和一致,可能会出现不同的行为。例如,如果捕获数据库错误并尝试继续,则会看到TransactionManagementError,而先前的行为未定义且可能与情况有关。

但是,如果您正确操作,一切都应该像以前一样运作。


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