什么是分布式事务处理
事务(transaction)处理是将多个操作作为一个单元处理的工作方式。也就是说可以认为在一个transaction里的多个操作的最终状态或者同时成功,或者同时失败并且回滚(rollback),不会出现部分操作成功部分操作失败的最终状态。在分布式系统(distributed system)里,事务处理体现为多个操作可以被不同的主机(host)或者节点(node)执行,其余方面和普通的transaction定义没有区别。关于transaction,有一个非常著名的ACID的要求。也就是一个transaction,应该满足下面四个方面的要求:
- Atomicity(原子性):事务是一组不可再分割,不可再简化的操作,它们或者全部发生,或者全部不发生
- Consistency(一致性):事务只可以将数据从一个正确状态转换到另一个正确状态
- Isolation(隔离性):并发的事务和顺序执行的事务会产生相同的数据结果
- Durability(持久性): 已提交的事务即便在系统失败的情况下仍然保持已提交状态
2PC和SAGA是实现分布式系统中事务处理的两种不同的设计模式,下面我们先分别简单介绍一下这两种模式。
2PC模式
2PC是two-phase commit的缩写,也就是两阶段提交。 顾名思义,这个模式分为两个阶段。
第一阶段为准备阶段。体统中存在一个协调者(coordinator)的角色。事务的请求首先发送给协调者。协调者需要知道具体一个事务的参与者(participant)有哪些。这些参与者可能是分布式系统中的一个主机,或者是一个node,或者是一个服务(service)。协调者会向所有参与者发送准备(prepare)的指示。参与者需要根据自己的状态判断是否可以确保执行该事务。这里的执行包括执行成功,或者执行失败时的rollback。如果可以执行,参与者就要保持可以执行的状态,并且向协调者发送准备好(prepared)的应答;否则参与者就要发送终止(abort)的应答。当所有的参与者都发送了应答,准备阶段结束,进入第二阶段。
第二阶段是提交(commit)阶段。假如有任何参与者发送了abort应答,协调者向所有参与者发送发送rollback指示,以使得系统rollback到事务执行之前的状态。假如所有的参与者都发送的prepared应答,协调者就向所有参与者发送提交指示。假如所有参与者都提交成功,则事务成功结束。如果有任何一个参与者提交失败,协调者向所有参与者发送rollback指示,确保系统可以回滚到事务执行之前的状态。
关于事务处理成功的序列图可以参考下图(引用自wiki):
关于事务处理终止的序列图可以参考下图(引用自wiki):
SAGA模式
Saga是另一种实现分布式事务的设计模式。Saga依赖于系统中的每个host(或者node,或者service)在独立的状态下支持事务的提交。每一个Saga系统的参与者需要提供Local Transaction以支持整体系统的事务的执行。整体系统以某种机制依次通知每个参与者执行自己的Local Transaction。假如某个参与者的Local Transaction执行失败,参与者需要自行修复被transaction执行影响的数据。具体来说Saga的参与者可以有三种Transaction:
- Compensable transaction(可补偿事务):是可以被另一个transaction的执行消除影响的transaction
- Pivot transaction(枢纽事务):是决定Saga的transaction是否要执行完成的transaction。即如果pivot transaction执行完成了,Saga的transaction就需要完全执行完;否则,Saga的transaction需要终止
- Retryable transaction(可重试事务):是在pivot transaction之后才执行的transaction。Retryable transaction可以重试,并且必须保证执行完成。
Saga的数据流图可以参考下图(引用自Azure网站):
Saga具体又可以分为Choreography模式和Orchestration模式。这里就不详细描述了。感兴趣的同学可以参考互联网上的文章。
两种模式的比较和思考
首先我们介绍一下两种模式的比较:
2PC依赖于所有参与者共同commit和rollback,所以整体系统的行为对外界是一直保持一致的。也就是说当系统处于事务执行的中间状态时,系统的访问者是感受不到的。系统的访问者只会访问到事务执行之前的状态和执行之后的状态。而Saga是依赖于参与者分别执行Local Transaction的,也就是说系统的访问者是会看到中间状态的,也就是说某些host已经完成了Local Transaction,某些host还没有。
相对应的我们可以认为2PC对于系统的可用性(availability)影响较大。因为任何一个参与者的失败就会造成整个系统事务的失败。假如参与者众多的话,可能系统事务执行成功的可能性会比较低。相反,Saga对系统的可用性较小,因为每一个参与者可以分别执行local transaction,相互之间的依赖和耦合度都小很多。
下面是我对于这种模式的一些思考:
2PC适用于参与者较少,事务执行简单快速,维护事务执行中间状态比较复杂的情况。而Saga适用于事务中间状态可以被系统访问者接受,事务执行时间较长的情况。在这里我们发现系统操作的幂等性(idempotent)对于Saga模式是至关重要的。我们考虑Saga系统中部分参与者participant执行成功,而部分执行失败的情况,假如系统操作支持幂等性,整个系统的状态就可以被下一次transaction的执行恢复到一致的状态,也就是说整个系统是可以按照eventually consistent的逻辑来设计和使用的。但是假如不是幂等的,Saga系统的恢复就非常困难。总体来说2PC比起Saga更容易理解和维护,但是2PC对于系统的要求也更高,体现在各个参与者与协调者之间的通信和同步上。相对来说Saga则更可以保证整个系统的可用性(availability)和健壮性(robust),当然这需要基于整个体统的设计是幂等的并且eventually consistent是可以被接受的。所以我个人认为我们在设计时应该首先考虑Saga,其次考虑2PC。当然具体的情况还要具体分析,毕竟所有的solution都是某些因素的折衷(trade off)方案,没有一个可以适用于所有情况的方案。同学们有什么问题或者见解欢迎发表评论。