7. 事务
一些作者声称,支持通用的两阶段提交代价太大,会带来性能与可用性的问题。让程序员来处理过度使用事务导致的性能问题,总比缺少事务编程好得多。 ——James Corbett 等人,Spanner:Google 的全球分布式数据库(2012)
在数据系统的残酷现实中,很多事情都可能出错:
- 数据库软件、硬件可能在任意时刻发生故障(包括写操作进行到一半时)。
- 应用程序可能在任意时刻崩溃(包括一系列操作的中间)。
- 网络中断可能会意外切断数据库与应用的连接,或数据库之间的连接。
- 多个客户端可能会同时写入数据库,覆盖彼此的更改。
- 客户端可能读取到无意义的数据,因为数据只更新了一部分。
- 客户之间的竞争条件可能导致令人惊讶的错误。
为了实现可靠性,系统必须处理这些故障,确保它们不会导致整个系统的灾难性故障。但是实现容错机制工作量巨大。需要仔细考虑所有可能出错的事情,并进行大量的测试,以确保解决方案真正管用。
数十年来, 事务(transaction) 一直是简化这些问题的首选机制。事务是应用程序将多个读写操作组合成一个逻辑单元的一种方式。从概念上讲,事务中的所有读写操作被视作单个操作来执行:整个事务要么成功( 提交(commit) )要么失败( 中止(abort) , 回滚(rollback) )。如果失败,应用程序可以安全地重试。对于事务来说,应用程序的错误处理变得简单多了,因为它不用再担心部分失败的情况了,即某些操作成功,某些失败(无论出于何种原因)。
和事务打交道时间长了,你可能会觉得它显而易见。但我们不应将其视为理所当然。事务不自然法;它们是为了 简化应用编程模型 而创建的。通过使用事务,应用程序可以自由地忽略某些潜在的错误情况和并发问题,因为数据库会替应用处理好这些。(我们称之为 安全保证(safety guarantees) )。
并不是所有的应用都需要事务,有时候弱化事务保证、或完全放弃事务也是有好处的(例如,为了获得更高性能或更高可用性)。一些安全属性也可以在没有事务的情况下实现。
怎样知道你是否需要事务?为了回答这个问题,首先需要确切理解事务可以提供的安全保障,以及它们的代价。尽管乍看事务似乎很简单,但实际上有许多微妙但重要的细节在起作用。
本章将研究许多出错案例,并探索数据库用于防范这些问题的算法。尤其会深入 并发控制 的领域,讨论各种可能发生的竞争条件,以及数据库如何实现 读已提交 , 快照隔离 和 可串行化 等隔离级别。
本章同时适用于单机数据库与分布式数据库;在 第 8 章 中将重点讨论仅出现在分布式系统中的特殊挑战。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论