13

如果没有事务,我不敢在数据库中做任何复杂的事情。几乎总是有一个简单易用的内置命令。但是,当您开始使用其他持久性数据时,您并没有得到这种简单易用的事务支持。一些例子是

  • 文件系统
  • 网络服务(我没有使用过)

即使在非持久性数据中,在出现异常后撤消工作块通常也很有用。使用一种语言获得的标准数据结构都不支持事务。

我想知道的是,为什么数据库是特例?

数据库之外的事务行为主题是否有任何有用的链接?

4

7 回答 7

16

我必须恭敬地不同意:事务系统不是自动和专有的数据库引擎,恰恰相反......

我已经实现了一种不同于数据库事务的应用程序事务机制(在 .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 人公司设计了一个关键业务系统的重写。我发现实施这样的机制是必要的,而且我发现这种体验很容易、很有价值并且很有意义。我会再做一次,也许更容易一些,但我总是会质疑为什么我不能只坚持一个数据库事务。

于 2008-11-17T23:19:08.813 回答
9

我认为事务只出现在数据库中的原因是,根据定义,提供事务的系统称为数据库。这听起来很循环,所以我必须详细说明。

事务支持是提供ACID属性的功能。用外行的话来说,这意味着一个事务允许 1. 将许多谨慎的操作捆绑到一个包中,这些操作要么作为一个整体成功,要么作为一个整体失败 2. 隐藏对并发用户的未提交更改,以便 3. 并发用户始终对系统有“一致”的看法。

文件系统传统上提供一些锁定机制,但这与提供事务不同。但是,所有文件系统都有一些原子属性。例如,如果您有目录/a/and /b/,并且您同时尝试执行mv /a /b/aand mv /b /a/b,那么这些操作中只有一个会成功,而不会影响完整性。然而,文件系统通常缺乏将多个操作捆绑到一个孤立的原子包中的能力。

一个答案提到了颠覆。所有健全的版本控制系统都有事务。当提交多个文件时,系统要么完全应用提交,要么完全拒绝它(除了 CVS,我不认为它是理智的)。拒绝的原因总是同时发生的变化。版本控制系统的实现者非常注意维护数据库。

另一个答案提到消息传递系统是事务性的。我没有阅读链接的材料,但答案本身只提到了消息的原子传递。那不是交易。

在Brian C.在这里提到它之前,我从未听说过Clojure 。在我看来,这确实是数据库上下文之外的事务实现。这里的重点是并发控制,而不是维护持久数据的一致性。

因此,除了 Clojure 之外,似乎任何需要事务的系统要么使用底层数据库,要么将自己变成数据库。

于 2008-11-17T21:52:30.360 回答
6

现代文件系统确实有事务。它们对最终用户是透明的。

NTFS、XFS、JFS、EXT3 和 ReiserFS 都可以,仅举几例。

这只是文件系统的内部。许多操作系统还支持文件锁定(例如,参见 *NIX 世界中的flock(2)),带有排他(写)和共享(读)锁。

编辑:如果您考虑一下,文件系统不像现代数据库那样具有隔离级别,因为一旦您完成读取文件,如果您没有锁定它,您通常会关闭它。然后当你想写它时重新打开它。

于 2008-11-17T21:04:22.687 回答
5

Clojure使用软件事务内存,它使用事务使编写多线程程序变得容易和安全,而无需手动锁定。Clojure 具有不可变的数据结构和对它们的可变引用,并且需要事务来更改引用。

于 2008-11-17T21:34:38.960 回答
4

消息系统是事务资源管理器的另一个例子。

也就是说,您可以确保消息使用者成功处理队列中的消息。如果处理失败,则消息留在队列中。

此外,消息传递系统可以参与与另一个资源管理器的分布式事务。

更多信息在

于 2008-11-17T21:07:58.203 回答
3

Subversion 提交是事务性的:它们是真正原子的,因此中断的提交不会使存储库处于不一致的状态。

于 2008-11-17T21:22:02.947 回答
1

我遇到了需要将文件系统和数据库视为一个事务单元的情况。

就我而言,我只需要将一组文件下载到文件系统中。我通过每次创建随机目录、将数据放在那里并将目录名称存储在数据库表中来做到这一点。因此,我所有的数据库工作以及数据库表中的目录名称(= 文件系统工作)都可以在一个数据库事务中完成。

http://www.databasesandlife.com/atomic-operations-over-filesystem-and-database/

于 2010-12-02T16:46:51.967 回答