第 7 章 交易 交易
本作品已使用人工智能进行翻译。欢迎您提供反馈和意见:translation-feedback@oreilly.com
一些作者声称,支持一般的两阶段提交代价太高,因为这会带来性能或可用性问题。我们认为,让应用程序程序员在瓶颈出现时处理因过度使用事务而导致的性能问题,比总是围绕缺乏事务进行编码要好得多。
James Corbett 等人,Spanner:谷歌的全球分布式数据库(2012 年)
在数据系统的残酷现实中,很多事情都可能出错:
-
数据库软件或硬件可能随时发生故障(包括在写操作过程中)。
-
应用程序可能随时崩溃(包括一系列操作进行到一半时)。
-
网络中断可能会意外切断应用程序与数据库的连接,或一个数据库节点与另一个数据库节点的连接。
-
多个客户端可能会同时向数据库写入内容,从而覆盖彼此的更改。
-
客户端可能会读取一些没有意义的数据,因为这些数据只更新了一部分。
-
客户端之间的竞赛条件可能会导致令人惊讶的错误。
为了做到可靠,系统必须处理这些故障,并确保它们不会导致整个系统出现灾难性故障。然而,实施容错机制是一项艰巨的工作。它需要认真思考所有可能出错的地方,并进行大量测试,以确保解决方案确实有效。
几十年来,事务一直是简化这些问题的首选机制。事务是应用程序将多个读写操作组合成一个逻辑单元的一种方式。从概念上讲,事务中的所有读写操作都是作为一个操作执行的:要么整个事务成功(提交),要么失败(中止、回滚)。如果失败,应用程序可以安全地重试。有了事务,应用程序的错误处理就变得简单多了,因为它不需要担心部分失败,即有些操作成功,有些操作失败(无论什么原因)。
如果你多年来一直在使用事务,那么它们可能看起来很明显,但我们不应该认为它们是理所当然的。事务并非自然法则,它的出现是有目的的,即简化访问数据库的应用程序的编程模型。通过使用事务,应用程序可以自由地忽略某些潜在的错误情况和并发问题,因为数据库会处理这些问题(我们称之为安全保证)。
并非每个应用程序都需要事务,有时削弱事务保证或完全放弃事务保证也有好处(例如,实现更高的性能或更高的可用性)。有些安全特性无需事务即可实现。
如何确定是否需要交易?要回答这个问题,我们首先需要了解交易到底能提供哪些安全保障,以及与之相关的成本是多少。虽然交易乍看之下简单明了,但实际上有许多微妙而重要的细节在起作用。
在本章中,我们将研究许多可能出错的实例,并探讨数据库用来防范这些问题的算法。我们将特别深入并发控制领域,讨论可能出现的各种竞赛条件,以及数据库如何实现读已提交、快照隔离和序列化等隔离级别。
本章既适用于单节点数据库,也适用于分布式数据库;在第 8 章中,我们将重点讨论只有在分布式系统中才会出现的特殊挑战。