凤凰架构:构建可靠的大型分布式系统
上QQ阅读APP看书,第一时间看更新

第3章 事务处理

事务处理几乎在每一个信息系统中都会涉及,它存在的意义是为了保证系统中所有的数据都是符合期望的,且相互关联的数据之间不会产生矛盾,即数据状态的一致性(Consistency)。按照数据库的经典理论,要达成这个目标,需要三方面共同努力来保障。

·原子性(Atomic):在同一项业务处理过程中,事务保证了对多个数据的修改,要么同时成功,要么同时被撤销。

·隔离性(Isolation):在不同的业务处理过程中,事务保证了各业务正在读、写的数据相互独立,不会彼此影响。

·持久性(Durability):事务应当保证所有成功被提交的数据修改都能够正确地被持久化,不丢失数据。

以上四种属性即事务的“ACID”特性,但笔者对这种说法其实不太认同,因为这四种特性并不正交,A、I、D是手段,C是目的,前者是因,后者是果,弄到一块去完全是为了拼凑个单词缩写。

事务的概念虽然最初起源于数据库系统,但今天已经有所延伸,不再局限于数据库本身了。所有需要保证数据一致性的应用场景,包括但不限于数据库、事务内存、缓存、消息队列、分布式存储,等等,都有可能用到事务,后文里笔者会使用“数据源”来泛指所有这些场景中提供与存储数据的逻辑设备,但是上述场景所说的事务和一致性含义可能并不完全一致,说明如下。

·当一个服务只使用一个数据源时,通过A、I、D来获得一致性是最经典的做法,也是相对容易的。此时,多个并发事务所读写的数据能够被数据源感知是否存在冲突,并发事务的读写在时间线上的最终顺序是由数据源来确定的,这种事务间一致性被称为“内部一致性”。

·当一个服务使用到多个不同的数据源,甚至多个不同服务同时涉及多个不同的数据源时,问题就变得困难了许多。此时,并发执行甚至是先后执行的多个事务,在时间线上的顺序并不由任何一个数据源来决定,这种涉及多个数据源的事务间一致性被称为“外部一致性”[1]

外部一致性问题通常很难使用A、I、D来解决,因为这样需要付出很大甚至不切实际的代价;但是外部一致性又是分布式系统中必然会遇到且必须要解决的问题,为此我们要转变观念,将一致性从“是或否”的二元属性转变为可以按不同强度分开讨论的多元属性,在确保代价可承受的前提下获得强度尽可能高的一致性保障,也正因如此,事务处理才从一个具体操作上的“编程问题”上升成一个需要全局权衡的“架构问题”。

人们在探索这些解决方案的过程中,产生了许多新的思路和概念,有一些概念看上去并不那么直观,在本章,笔者会通过同一个场景事例讲解如何在不同的事务方案中贯穿、理顺这些概念。

额外知识

场景事例

Fenix’s Bookstore是一个在线书店。当一本书被成功售出时,需要确保以下三件事情被正确地处理:

·用户的账号扣减相应的商品款项;

·商品仓库中扣减库存,将商品标识为待配送状态;

·商家的账号增加相应的商品款项。

接下来,笔者将逐一介绍在“单个服务使用单个数据源”“单个服务使用多个数据源”“多个服务使用单个数据源”以及“多个服务使用多个数据源”下,可以采用哪些手段来保证数据在以上场景中被正确地读写。

[1] 外部一致性的定义起源于Google的Spanner的论文,地址为https://cloud.google.com/spanner/docs/true-time-external-consistency。