深入理解分布式事务
一、引言
分布式事务是指在网络中一个或多个主机上的数据库事务的调用,即一个分布式事务包括一个或多个,独立或一组操作,用于更新两个或更多不同分布式数据库节点上的数据。
分布式事务相对单节点数据库事务而言,在分布式数据库中,确保数据的执行与单节点数据库事务执行的效果一致,满足
ACID (atomicity, consistency, isolation, durability) 原则。
在进一步理解分布式事务要求前,我们先了解一下分布式系统背景下几个重要的理论。
ACID原则
通常关系型数据都满足,即
1.原子性(Atomicity),整个事务中的所有操作,要么全部完成,要么全部失败,不可能停滞在中间某个环节。事务在执行过程中发生错误,会被回滚到事务开始前的状态,就像这个事务从来没有执行过一样。
2.一致性(Consistency),在事务开始前和事务结束后,数据库的完整性约束没有被破坏
3.隔离性(Isolation),两个事务执行是互不干扰的,一个事务不可能看到其他事务运行时间中某一时刻的数据。两个事务两个发生交互
4.持久性(Durability),在事务完成以后,该事务对数据库所做的更改便持久地保存在数据库中,并不会被回滚。
事务的ACID属性保证了数据库的一致性。
CAP理论
CAP定理是由加州大学伯克利分校Eric Brewer教授提出来的,指出分布式系统中无法同时满足以下3个属性:
1.一致性(Consistency): 在分布式系统中的所有数据,在同一时刻保持一样。
2.可用性(Availablility):在集群中一部分节点故障后,集群整体是否能响应客户端的读写请求。
3.分区容错性(Partition toleration):分布式系统的容错性。节点crash或者网络分片都不应该导致一个分布式系统停止服务
基本CAP的证明思路CAP的证明基于异步网络,异步网络也是反映了真实网络中情况的模型。真实的网络系统中,节点之间不可能保持同步,即便是时钟也不可能保持同步,所有的节点依靠获得的消息来进行本地计算和通讯。这个概念其实是相当强的,意味着任何超时判断也是不可能的,因为没有共同的时间标准。之后我们会扩展CAP的证明到弱一点的异步网络中,这个网络中时钟不完全一致,但是时钟运行的步调是一致的,这种系统是允许节点做超时判断的。
CAP的证明很简单,假设两个节点集{G1, G2},由于网络分片导致G1和G2之间所有的通讯都断开了,如果在G1中写,在G2中读刚写的数据, G2中返回的值不可能G1中的写值。由于A的要求,G2一定要返回这次读请求,由于P的存在,导致C一定是不可满足的。
CAP三者不可兼得,两类组合的特性、使用场景以及典型产品对比如下:
BASE理论
eBay的架构师Dan Pritchett源于对大规模分布式系统的实践总结,在ACM上发表文章提出BASE理论,BASE理论是对CAP理论的延伸,核心思想是即使无法做到强一致性(Strong Consistency,CAP的一致性就是强一致性),但应用可以采用适合的方式达到最终一致性(Eventual Consitency)
1.Basic Availability:基本可用,这意味着,系统可以出现暂时不可用的状态,而后面会快速恢复
2.Soft-state:软状态,为了提高性能,我们让服务暂时保存一些状态或数据,这些状态和数据不是强一致性的
3.Eventual Consistency:最终一致性,系统在一个短暂的时间内是不一致的,但最终整个系统看到的数据是一致性的。
二、分布式事务常用解决方案
1. 两阶段提交(2PC)
两阶段提交(2PC,two-phase commit),通过两个阶段来实现分布式事务:
- 第一个阶段(proposal),协调器发消息,向其他参与的服务发送数据,各节点进行预提交,并接受各个服务节点的响应信息
- 第二个阶段(commit-or-abort), 协调器再次与其他各个参与的服务,要么全部提交(commit),要么全部取消(abort)
这个处理过程中的协调器(coordinator)不是提前选出来的,任何一个节点都可以作为Coordinator,如果这个节点需要开启事务,因此协调器来初始化两阶段提交。
如果第一阶段,所有的参与者都发送了yes ,那么 协调者将发送 commit 告诉所有参与者进行提交;反之,如果有一个参与者发送no,那么协调者将发送abort告诉所有参与者进行撤销。
然而,上述2PC提交中存在异常或网络问题影响整个事务执行,例如,其中某个参与者宕机,那么协调器将无法收到所有参与者的投票,将会阻塞整个事务执行,称为(fail-stop 模型);此外,还有可能参与者宕机后恢复启动,仍然继续执行,从而影响事务执行,称为(fail-recovery 模型)。
一种解决方式,添加一个
优点:两阶段提交存在很强的一致性
缺点:实现复杂,与业务进耦合,对性能影响较大,不适合高并发场景。
2. XA X/Open
X/Open XA (eXtanded Architecture)标准由X/Open组织于1991年发布的应用于分布式事务处理(DTP,distributed transaction processing)。
XA是基于两阶段提交2PC协议实现的,它规定了要实现分布式事务,需要三种角色:
AP:Application 应用系统 它就是我们开发的业务系统,在我们开发的过程中,可以使用资源管理器提供的事务接口来实现分布式事务。
TM:Transaction Manager 事务管理器分布式事务的实现由事务管理器来完成,它会提供分布式事务的操作接口供我们的业务系统调用。这些接口称为TX接口。事务管理器还管理着所有的资源管理器,通过它们提供的XA接口来同一调度这些资源管理器,以实现分布式事务。
RM:Resource Manager 资源管理器能够提供数据服务的对象都可以是资源管理器,比如:数据库、消息中间件、缓存等。大部分场景下,数据库即为分布式事务中的资源管理器。资源管理器能够提供单数据库的事务能力,它们通过XA接口,将本数据库的提交、回滚等能力提供给事务管理器调用,以帮助事务管理器实现分布式的事务管理。
3. 三阶段提交(3PC)
3PC(three phase commit)即三阶段提交[13],既然2PC可以在异步网络+节点宕机恢复的模型下实现一致性,那还需要3PC做什么,3PC是什么鬼?
在2PC中一个participant的状态只有它自己和coordinator知晓,假如coordinator提议后自身宕机,在watchdog启用前一个participant又宕机,其他participant就会进入既不能回滚、又不能强制commit的阻塞状态,直到participant宕机恢复。这引出两个疑问:能不能去掉阻塞,使系统可以在commit/abort前回滚(rollback)到决议发起前的初始状态当次决议中,participant间能不能相互知道对方的状态,又或者participant间根本不依赖对方的状态&n
相比2PC,3PC增加了一个准备提交(prepare to commit)阶段来解决以上问题:
图片截取自wikipedia
coordinator接收完participant的反馈(vote)之后,进入阶段2,给各个participant发送准备提交(prepare to commit)指令。participant接到准备提交指令后可以锁资源,但要求相关操作必须可回滚。coordinator接收完确认(ACK)后进入阶段3、进行commit/abort,3PC的阶段3与2PC的阶段2无异。协调者备份(coordinator)、状态记录(logging)同样应用在3PC。
participant如果在不同阶段宕机,我们来看看3PC如何应对:
- 阶段1: coordinator或watchdog未收到宕机participant的vote,直接中止事务;宕机的participant恢复后,读取logging发现未发出赞成vote,自行中止该次事务
- 阶段2: coordinator未收到宕机participant的precommit ACK,但因为之前已经收到了宕机participant的赞成反馈(不然也不会进入到阶段2),coordinator进行commit;watchdog可以通过问询其他participant获得这些信息,过程同理;宕机的participant恢复后发现收到precommit或已经发出赞成vote,则自行commit该次事务
- 阶段3: 即便coordinator或watchdog未收到宕机participant的commit ACK,也结束该次事务;宕机的participant恢复后发现收到commit或者precommit,也将自行commit该次事务因为有了准备提交(prepare to commit)阶段,3PC的事务处理延时也增加了1个RTT,变为3个RTT(propose+precommit+commit),但是它防止participant宕机后整个系统进入阻塞态,增强了系统的可用性,对一些现实业务场景是非常值得的。
3.补偿事务(TCC)
TCC 其实就是采用的补偿机制,其核心思想是:针对每个操作,都要注册一个与其对应的确认和补偿(撤销)操作。它分为三个阶段:Try 阶段主要是对业务系统做检测及资源预留Confirm 阶段主要是对业务系统做确认提交,Try阶段执行成功并开始执行 Confirm阶段时,默认 Confirm阶段是不会出错的。即:只要Try成功,Confirm一定成功。Cancel 阶段主要是在业务执行错误,需要回滚的状态下执行的业务取消,预留资源释放。举个例子,假入 Bob 要向 Smith 转账,思路大概是:
我们有一个本地方法,里面依次调用
1、首先在 Try 阶段,要先调用远程接口把 Smith 和 Bob 的钱给冻结起来。
2、在 Confirm 阶段,执行远程调用的转账的操作,转账成功进行解冻。
3、如果第2步执行成功,那么转账成功,如果第二步执行失败,则调用远程冻结接口对应的解冻方法 (Cancel)。
优点: 跟2PC比起来,实现以及流程相对简单了一些,但数据的一致性比2PC也要差一些
缺点: 缺点还是比较明显的,在2,3步中都有可能失败。TCC属于应用层的一种补偿方式,所以需要程序员在实现的时候多写很多补偿的代码,在一些场景中,一些业务流程可能用TCC不太好定义及处理
4.本地消息(异步确保)
本地消息表这种实现方式应该是业界使用最多的,其核心思想是将分布式事务拆分成本地事务进行处理,这种思路是来源于ebay。我们可以从下面的流程图中看出其中的一些细节:
基本思路就是:消息生产方,需要额外建一个消息表,并记录消息发送状态。消息表和业务数据要在一个事务里提交,也就是说他们要在一个数据库里面。然后消息会经过MQ发送到消息的消费方。如果消息发送失败,会进行重试发送。
消息消费方,需要处理这个消息,并完成自己的业务逻辑。此时如果本地事务处理成功,表明已经处理成功了,如果处理失败,那么就会重试执行。如果是业务上面的失败,可以给生产方发送一个业务补偿消息,通知生产方进行回滚等操作。
生产方和消费方定时扫描本地消息表,把还没处理完成的消息或者失败的消息再发送一遍。如果有靠谱的自动对账补账逻辑,这种方案还是非常实用的。
这种方案遵循BASE理论,采用的是最终一致性,笔者认为是这几种方案里面比较适合实际业务场景的,即不会出现像2PC那样复杂的实现(当调用链很长的时候,2PC的可用性是非常低的),也不会像TCC那样可能出现确认或者回滚不了的情况。
优点:一种非常经典的实现,避免了分布式事务,实现了最终一致性。
缺点:消息表会耦合到业务系统中,如果没有封装好的解决方案,会有很多杂活需要处理。
5.MQ事务消息(基于可靠消息服务的分布式事务)
有一些第三方的MQ是支持事务消息的,比如RocketMQ,他们支持事务消息的方式也是类似于采用的二阶段提交,但是市面上一些主流的MQ都是不支持事务消息的,比如 RabbitMQ 和 Kafka 都不支持。
以阿里的 RocketMQ 中间件为例,其思路大致为:
1、发送方将半事务消息发送至消息队列 RocketMQ 版服务端。
2、消息队列 RocketMQ 版服务端将消息持久化成功之后,向发送方返回 Ack 确认消息已经发送成功,此时消息为半事务消息。
3、发送方开始执行本地事务逻辑。
4、发送方根据本地事务执行结果向服务端提交二次确认(Commit 或是 Rollback),服务端收到 Commit 状态则将半事务消息标记为可投递,订阅方最终将收到该消息;服务端收到 Rollback 状态则删除半事务消息,订阅方将不会接受该消息。
详细: https://rocketmq.apache.org/docs/transaction-example/
https://help.aliyun.com/document_detail/43348.html
优点:;实现了最终一致性,不需要依赖本地数据库事务。
缺点:实现难度大,主流MQ不支持,没有.NET客户端,RocketMQ事务消息部分代码也未开源。
6.Sagas事务模型
1987年普林斯顿大学的Hector Garcia-Molina和Kenneth Salem发表了一篇论文 《SAGAS》,讲述的是如何处理长时间运行的事务(Long-running-transaction)。Saga是一个长活事务可被分解成可以交错运行的子事务集合。其中每个子事务都是一个保持数据库一致性的真实事务
Saga事务模型又叫做长时间运行的事务(Long-running-transaction), 它是由普林斯顿大学的H.Garcia-Molina等人提出,它描述的是另外一种在没有两阶段提交的的情况下解决分布式系统中复杂的业务事务问题。
你可以在这里看到 Sagas 相关论文。我们这里说的是一种基于 Sagas 机制的工作流事务模型,这个模型的相关理论目前来说还是比较新的,以至于百度上几乎没有什么相关资料。
该模型其核心思想就是拆分分布式系统中的长事务为多个短事务,或者叫多个本地事务,然后由 Sagas 工作流引擎负责协调,如果整个流程正常结束,那么就算是业务成功完成,如果在这过程中实现失败,那么Sagas工作流引擎就会以相反的顺序调用补偿操作,重新进行业务回滚。比如我们一次关于购买旅游套餐业务操作涉及到三个操作,他们分别是预定车辆,预定宾馆,预定机票,他们分别属于三个不同的远程接口。可能从我们程序的角度来说他们不属于一个事务,但是从业务角度来说是属于同一个事务的。
他们的执行顺序如上图所示,所以当发生失败时,会依次进行取消的补偿操作。因为长事务被拆分了很多个业务流,所以 Sagas 事务模型最重要的一个部件就是工作流或者你也可以叫流程管理器(Process Manager),工作流引擎和Process Manager虽然不是同一个东西,但是在这里,他们的职责是相同的。在选择工作流引擎之后,最终的代码也许看起来是这样的
三、总结
在分布式系统中很难实现强一致的事务,因而更多情况基于BASE理论实现分布式事务。提高系统的可用性,确保数据最终一致。几种分布式事务方法各有优缺点,需要结合实际业务场景综合考虑做出选择。
四、业界分布事务框架
华为 servicecomb-pack
https://blog.csdn.net/kisscatforever/article/details/83585077
Seata分布式事务框架,是阿里巴巴开源的分布式事务中间件,一种分布式事务解决方案,具有高性能和易于使用的微服务架构,前身fescar https://github.com/seata/seata
教程 http://www.pbteach.com/post/java_distribut/subject_dtx-02/
对比参考
| 框架名称 | GitHub地址 | Star数量 (2020-2-24) | 备注 |
|---|---|---|---|
| Seata | https://github.com/seata/seata | 14.3k | 阿里巴巴 |
| tcc-transaction | https://github.com/changmingxie/tcc-transaction | 2.4k | |
| Hmily | https://github.com/yu199195/hmily | 2.7k | |
| ByteTCC | https://github.com/liuyangming/ByteTCC | 2.2k | |
| myth | https://github.com/yu199195/myth | 1.4k | |
| EasyTransaction | https://github.com/QNJR-GROUP/EasyTransaction | 2k | |
| tx-lcn | https://github.com/codingapi/tx-lcn/ | 3.3k | |
| servicecomb-pack | https://github.com/apache/servicecomb-pack | 1.7k | 华为 |
http://springcloud.cn/view/374
参考
[1] Wikipedia Distributed Transaction https://en.wikipedia.org/wiki/Distributed_transaction
[2] Oracle What Are Distributed Transactions? https://docs.oracle.com/cd/B28359_01/server.111/b28310/ds_txns001.htm#ADMIN1221
[3] Seth Gilbert and Nancy Lynch. 2002. Brewer’s conjecture and the feasibility of consistent, available, partition-tolerant web services. SIGACT News 33, 2 (June 2002), 51-59. DOI=10.1145/564585.564601 http://doi.acm.org/10.1145/564585.564601
[4] Guy Pardon, 2008, A CAP Solution (Proving Brewer Wrong), http://blog.atomikos.com/2008/09/a-cap-solution-proving-brewer-wrong/
[5] Werner Vogels, 2007, Availability & Consistency, http://www.infoq.com/presentations/availability-consistency
[6] Nathan Marz, 2011, How to beat the CAP theorem, http://nathanmarz.com/blog/how-to-beat-the-cap-theorem.html
[7] CAP理论 https://www.cnblogs.com/mmjx/archive/2011/12/19/2290540.html
[8] 分布式事务 https://www.cnblogs.com/savorboard/p/distributed-system-transaction-consistency.html
[9] Solved Problems, Unsolved Problems and Problems in Concurrency, Leslie Lamport, 1983
[10] The Byzantine Generals Problem, Leslie Lamport,Robert Shostak and Marshall Pease, 1982
[11] Impossibility of Distributed Consensus with One Faulty Process, Fischer, Lynch and Patterson, 1985
[12] FLP Impossibility的证明, Daniel Wu, 2015[5] Consensus Protocols: Two-Phase Commit, Henry Robinson, 2008
[13] Consensus Protocols: Three-phase Commit, Henry Robinson, 2008
[14] Three-phase commit protocol, Wikipedia
[15] Sagas https://www.cs.cornell.edu/andru/cs711/2002fa/reading/sagas.pdf
[16] Sagas事务 https://www.jianshu.com/p/e4b662407c66