分布式事务

在数据系统的残酷现实中,很多事情都可能出错:

  • 数据库软件、硬件可能在任意时刻发生故障(包括写操作进行到一半时)。
  • 应用程序可能在任意时刻崩溃(包括一系列操作的中间)。
  • 网络中断可能会意外切断数据库与应用的连接,或数据库之间的连接。
  • 多个客戶端可能会同时写入数据库,覆盖彼此的更改。
  • 客戶端可能读取到无意义的数据,因为数据只更新了一部分。
  • 客戶端之间的竞争条件可能导致令人惊讶的错误。

为了实现可靠性,系统必须处理这些故障,确保它们不会导致整个系统的灾难性故障。但是实现容错机制工作量巨大。需要仔细考虑所有可能出错的事情,并进行大量的测试,以确保解决方案真正管用。

数十年来, 事务(transaction) 一直是简化这些问题的首选机制。事务是应用程序将多个读写操作组合成一个逻辑单元的一种方式。从概念上讲,事务中的所有读写操作被视作单个操作来执行:整个事务要么成功 提交 (commit),要么失败 中止 (abort)或 回滚 (rollback)。如果失败,应用程序可以安全地重试。对于事务来说,应用程序的错误处理变得简单多了,因为它不用再担心部分失败的情况了,即某些操作成功,某些失败(无论出于何种原因)。

和事务打交道时间⻓了,你可能会觉得它显而易⻅。但我们不应将其视为理所当然。事务不是天然存在的;它们是为了 简化应用编程模型 而创建的。通过使用事务,应用程序可以自由地忽略某些潜在的错误情况和并发问题,因为数据库会替应用处理好这些。(我们称之为 安全保证 ,即safety guarantees)。

并不是所有的应用都需要事务,有时候弱化事务保证、或完全放弃事务也是有好处的(例如,为了获得更高性能或更高可用性)。一些安全属性也可以在没有事务的情况下实现。

怎样知道你是否需要事务?为了回答这个问题,首先需要确切理解事务可以提供的安全保障,以及它们的代价。尽管乍看事务似乎很简单,但实际上有许多微妙但重要的细节在起作用。

事务的概念

现今,几乎所有的关系型数据库和一些非关系数据库都支持 事务 。其中大多数遵循IBMSystemR(第一个SQL数据库)在1975年引入的⻛格【1,2,3】。40年里,尽管一些实现细节发生了变化,但总体思路大同小异:MySQL、PostgreSQL、Oracle和SQLServer等数据库中的事务支持与SystemR异 乎寻常地相似。

2000年以后,非关系(NoSQL)数据库开始普及。它们的目标是在关系数据库的现状基础上,通过提供新的数据模型选择并默认包含复制和分区来进一步提升。事务是这次运动的主要牺牲品:这些新一代数据库中的许多数据库完全放弃了事务,或者重新定义了这个词,描述比以前所理解的更弱得多的一套保证。

随着这种新型分布式数据库的炒作,人们普遍认为事务是可伸缩性的对立面,任何大型系统都必须放弃事务以保持良好的性能和高可用性。另一方面,数据库厂商有时将事务保证作为“重要应用”和“有价值数据”的基本要求。这两种观点都是 纯粹的夸张

事实并非如此简单:与其他技术设计选择一样,事务有其优势和局限性。为了理解这些权衡,让我们了解事务所提供保证的细节⸺无论是在正常运行中还是在各种极端(但是现实存在)的情况下。