我必须恭敬地不同意:事务系统不是自动和排他的数据库引擎,恰恰相反......
我已经实现了一种不同于数据库事务的应用程序事务机制(在 .NET 中)。它实际上相当容易(包括单元测试套件在内的几个小时的工作)。它完全用 C# 编写,不依赖于任何数据库功能或任何其他组件。但首先是一些背景......
这种非数据库事务特性存在于 Java 平台上的多种表现形式中,例如 EJB、ESB、JMS,并且通常与 BPM 相关联。其中一些表现形式使用底层数据库,但并非总是如此,也并非没有必要。其他平台也有类似的表现形式,例如 MSMQ。
大多数旧版本控制系统不实现 ACID 事务语义。正如 ddaa 所说,CVS 没有,但 Subversion(它的继任者)有。 Visual Source Safe 没有。如果您研究 Subversion,您可以找到说明这一点的比较图表。
现在关键点是,数据库事务或其等价物并不能保证安全的业务逻辑。虽然我喜欢 Subversion,但具有讽刺意味的是,它是这一事实的一个很好的例子。
您可以虔诚地使用 Subversion 以及自动构建脚本(一个用于编译、测试和打包您的应用程序的命令),并且仍然将损坏的构建提交到源代码控制存储库。我已经多次看到了。当然,使用非 ACID 事务的源代码控制工具(如 VSS)更容易。但是很多人都震惊地发现可以使用 Subversion 之类的工具来实现这一点。
请允许我布置场景。您和一位同事正在开发一个应用程序,并使用 Subversion 作为源代码控制存储库。你们俩都在编码,偶尔会提交到存储库。您进行一些更改,运行一个干净的构建(重新编译所有源文件),并且所有测试都通过了。因此,您提交更改并回家。您的同事一直在进行自己的更改,因此他还运行了一个干净的构建,看到所有测试通过,并提交到存储库。但是,您的同事随后从存储库更新,进行了一些更改,运行干净的构建,构建在他的脸上炸毁了!他恢复他的更改,再次从存储库更新(只是为了确定),并发现一个干净的构建仍然爆炸!您的同事在接下来的几个小时内对构建和源代码进行故障排除,最终发现您在离开之前所做的更改导致构建失败。他向您和您的共同老板发送了一封令人讨厌的电子邮件,抱怨您破坏了构建然后粗心大意地回家了。你早上到达,发现你的同事和老板在你的办公桌前等着骂你,其他人都在看着!因此,您可以快速运行一个干净的构建,并向他们展示构建没有损坏(所有测试都通过了,就像昨晚一样)。
那么,这怎么可能呢?这是可能的,因为每个开发人员的工作站都不是 ACID 事务的一部分; Subversion 只保证存储库的内容。当您的同事从存储库更新时,他的工作站包含存储库内容(包括您的更改)和他自己未提交的更改的混合副本。当你的同事在他的工作站上运行一个干净的构建时,他正在调用一个不受 ACID 语义保护的业务事务。当他恢复更改并执行更新时,他的工作站随后与存储库匹配,但构建仍然中断。为什么?因为您的工作站也是不受 ACID 语义保护的单独业务事务的一部分,这与您对存储库的提交不同。由于在运行干净构建之前您没有更新工作站以匹配存储库,因此您实际上并没有构建存储库中存在的源文件。如果您执行了这样的更新,您会发现您的工作站上的构建也会失败。
现在我可以阐述我的初始观点了——交易具有必须仔细考虑的范围/上下文。仅仅因为您有 ACID 事务并不意味着您的业务逻辑是安全的,除非 ACID 事务的范围/上下文和业务逻辑完全匹配。如果您依赖于某种形式的数据库 ACID 事务,但您在业务逻辑中执行了该数据库事务未涵盖的任何操作,那么您的差距可能会导致类似的灾难性错误。如果您可以强制您的业务逻辑与您的数据库事务完全匹配,那么一切都很好。如果没有,那么您可能需要单独的业务交易。根据未受保护逻辑的性质,您可能需要实现自己的事务机制。
因此,消息传递可以是事务性的,但范围仅仅是消息。关于上面的例子,Subversion 的上下文只是对存储库的单个提交。但是,业务事务是一个干净的构建,涉及的范围要大得多。这个特定问题通常通过编写干净的构建脚本和干净的检出脚本来解决,理想情况下使用持续集成实现(例如,通过 CruiseControl 等)。在开发人员工作站上,它要求每个开发人员在干净构建之前执行完整更新(甚至是干净检查)。
因此,回顾一下,每个事务都有一个限制其保护的范围或上下文。业务事务通常包含超出我们常用的事务机制(例如数据库引擎)范围的逻辑。您可能必须弥补差额。在极少数情况下,编写自己的事务机制甚至可能是有意义的。
我为一家规模不大的 90 人公司构建了一个关键业务系统的重写架构。我发现实施这样的机制是必要的,而且我发现这种体验很容易、很有价值并且很有意义。我会再做一次,也许更容易一些,但我总是会质疑为什么我不能只坚持一个数据库事务。