分布式事务的几种解决方案

分布式事务的几种解决方案

  • 事务理论
  • BASE理论
  • 解决方案
    • 2PC - 两阶段提交协议
    • 3PC - 两阶段提交协议
    • TCC - 事务
    • SAGA - 事务
    • AT - 事务

当架构从单体走向分布式,特别是转向微服务架构。随之而来就必然遇到分布式事务这个难题,什么是分布式事务?就是在分布式系统中运行的事务,使用多个数据源,由多个本地事务组合而成。在分布式场景下,对事务的处理操作可能来自不同的机器,甚至是来自不同的操作系统,保证数据一致性。

整理的方案不代表全部,全属方便翻阅

事务理论

事务具有 4 个属性:原子性、一致性、隔离性、持久性。这四个属性通常称为 ACID 特性。

  • Atomicity(原子性):一个事务中的所有操作,要么全部完成,要么全部不完成,不会结束在中间某个环节。事务在执行过程中发生错误,会被恢复到事务开始前的状态,就像这个事务从来没有执行过一样。

  • Consistency(一致性):在事务开始之前和事务结束以后,数据库的完整性没有被破坏。完整性包括外键约束、应用定义的等约束不会被破坏。

  • Isolation(隔离性):数据库允许多个并发事务同时对其数据进行读写和修改的能力,隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致。

  • Durability(持久性):事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。

BASE理论

BASE是最基本可用,软状态,最终一致性。是对CAP中一致性和可用性权限的结果,是基于CAP定理演化而来,核心思想是即使无法做到强一直性,但每个应用都可以根据自身的业务特定,采用适当的方式来使系统达到最终一致性。

解决方案

  • 2PC - 二阶段提交算法
  • 3PC - 三阶段提交算法
  • TCC - 事务
  • SAGA事务
  • AT事务

2PC - 两阶段提交协议

事务管理器分两个阶段来协调资源管理器,第一阶段准备资源,也就是预留事务所需的资源,如果每个资源管理器都资源预留成功,则进行第二阶段资源提交,否则协调资源管理器回滚资源。

2PC协议的核心是,划分出了事务参与者和协调者的角色,并将整个过程划分成两个阶段。

阶段一:提交事务请求

  • 事务询问:协调者向所有的参与者发送事务内容,询问是否可以执行事务提交操作,并开始等待各参与者的相应。
  • 执行事务:各参与者节点执行事务操作,并将Undo和Redo信息计入事务日志中。
  • 如果参与者成功执行事务操作,就反馈给协调者Yes响应,表示事务可以执行,如果没有成功执行事务,就反馈给协调者No响应,表示事务不可执行。
  • 二阶段提交一些的阶段一也被称为投票阶段,即各参与者投票表明是否可以继续执行接下去的事务提交操作。

阶段二:执行事务提交

  • 假如协调者从所有的参与者获得反馈都是Yes响应,那么就会执行事务提交。
  • 发送提交请求:协调者向所有参与者节点发出Commit请求。
  • 事务提交:参与者接收到Commit请求后,会正式执行事务提交操作,并在完成提交之后放弃整个事务期间占用的事务资源。
  • 反馈事务提交结果:参与者在完成事务提交之后,向协调者发送ACK消息
  • 完成事务:协调者接收到所有参与者反馈的ACK消息后,完成事务。

中断事务

  • 假如任何一个参与者向协调者反馈了No响应,或者在等待超时之后,协调者尚无法接收到所有参与者的反馈响应,那么就中断事务。
  • 发送回滚请求:协调者向所有参与者节点发出Rollback请求。
  • 事务回滚:参与者接收到Rollback请求后,会利用其在阶段一中记录的Undo信息执行事务回滚操作,并在完成回滚之后释放事务执行期间占用的资源。
  • 反馈事务回滚结果:参与者在完成事务回滚之后,向协调者发送ACK消息。
  • 中断事务:协调者接收到所有参与者反馈的ACK消息后,完成事务中断。

缺点

  • 同步阻塞:参与该事务的逻辑,在事务处理期间都将是阻塞的。
  • 太过保守:一个节点出现一点问题,那么整个所有的节点都需要回滚(缺乏容错机制)。
  • 单点问题:2PC的整个流程过于依赖协调者,如果协调者第二阶段发生了问题,那么当前事务相关的所有参与者都将处于阻塞状态,无法完成事务操作。
  • 脑裂问题:可能由于网络原因,网络不好的节点没有进行事务处理,而网络正常的节点进行了事务处理,造成了数据不一致。

3PC - 两阶段提交协议

与两阶段提交不同的是,三阶段提交有两个改动点。

  • 引入超时机制。同时在协调者和参与者中都引入超时机制。

  • 在第一阶段和第二阶段中插入一个准备阶段。保证了在最后提交阶段之前各参与节点的状态是一致的。

也就是说,除了引入超时机制之外,3PC把2PC的准备阶段再次一分为二,这样三阶段提交就有CanCommit、PreCommit、DoCommit三个阶段。

阶段1:CanCommit

  • 事务询问:协调者向参与者发送一个CanCommit事务请求,询问是否可以执行事务提交操作,开始等待参与者的响应。
  • 参与者向协调者反馈事务询问响应:参与者根据自身情况,反馈YES响应,进入预备状态,否则返回NO响应。

阶段2:PreCommit

  • 返回yes情况:
    协调者向所有参与者发送PreCommit请求,进入准备阶段。
    参与者收到PreCommit请求后,执行事务操作,将undo和redo信息记录到日志中。
    各参与者向协调者反馈事务执行响应:成功响应ACK,同时等待最终指令:提交还是终止。
  • 返回no情况(中断事务):
    任一参与者向协调者反馈了NO响应或者等待超时之后,协调者无法接收到所有参与者反馈响应,那么中断事务。
    发送中断请求(abort请求)。
    参与者收到协调者abort请求或者等待协调者请求超时,参与者中断事务。

阶段3:DoCommit

  • 同步处理:
    发送提交请求:协调者正常工作状态,接收到来自所有参与者的ACK响应,那么它将从预提交状态转换为提交状态,向所有参与者发送DoCommit请求。
    事务提交:参与者收到DoCommit请求后,会正式执行事务提交操作,完成提交后,释放事务资源。
    反馈事务提交结果:参与者完成事务提交之后,向协调者发送ACK消息。
    完成事务:协调者收到所有参与者反馈的ACK消息后,完成事务。
  • 回滚处理:
    发送中断请求:协调者向所有参与者发送abort请求。
    事务回滚:参与者收到abort请求后,利用 阶段2中的undo日志来执行事务回滚操作,完成回滚,释放资源。
    反馈事务回滚结果:参与者完成了事务回滚后,向协调者发送ACK消息。
    协调者收到所有参与者反馈的ACK消息后,中断事务。

3PC优缺点:

  • 优点:减小了阶段阻塞的范围。
  • 缺点:当第二阶段PreCommit成功之后,第三阶段协调者向参与者发送DoCommit之后,然后参与者同步,但是如果同步之后,参与者与协调者断开了。那么协调者一直收不到参与者回复,那么就超时。协调者开始发送Abort回滚操作,其他参与者都回滚了,而断开的参与者没有回滚,造成了服务器节点之间数据不一致。

TCC - 事务

  • TCC是服务化的两阶段编程模型,其Try、Confirm、Cancel,3个方法均由业务编码实现。
  • TCC要求每个分支事务实现三个操作:预处理Try,确认Confirm,撤销Cancel。
  • Try操作做业务检查及资源预留。
  • Confirm做业务确认操作。
  • Cancel实现一个与Try相反的操作即回滚操作。
  • TM首先发起所有的分支事务Try操作,任何一个分支事务的Try操作执行失败,TM将会发起所有分支事务的Cancel操作,若Try操作全部成功,TM将会发起所有分支事务的Confirm操作,其中Confirm/Cancel操作若执行失败,TM会进行重试。

TCC的三个阶段

  • Try阶段是做业务检查(一致性)及资源预留(隔离),此阶段仅是一个初步操作,它和后续的Confirmy一起才能构成一个完整的业务逻辑。
  • Confirm阶段是做确认提交,Try阶段所有分支事务执行成功后开始执行Confirm,通常情况下,采用TCC则认为Confirm阶段是不会出错的,即:只要Try成功,Confirm一定成功,若Confirm阶段真的出错,需要引入重试机制或人工处理。
  • Cancel阶段是在业务执行错误需要回滚到状态下执行分支事务的取消,预留资源的释放,通常情况下,采用TCC则认为Cancel阶段也一定是真功的,若Cance阶段真的出错,需要引入重试机制或人工处理。
  • TM事务管理器:TM事务管理器可以实现为独立的服务,也可以让全局事务发起方充当TM的角色,TM独立出来是为了公用组件,是为了考虑系统结构和软件的复用。
  • TM在发起全局事务时生成全局事务记录,全局事务ID贯穿整个分布式事务调用链条,用来记录事务上下文,追踪和记录状态,用于Confirm和cacel失败需要进行重试,因此需要实现幂等。

TCC的三种异常处理情况

幂等处理

  • 因为网络抖动等原因,分布式事务框架可能会重复调用同一个分布式事务中的一个分支事务的二阶段接口。所以分支事务的二阶段接口Confirm/Cancel需要能够保证幂等性。如果二阶段接口不能保证幂等性,则会产生严重的问题,造成资源的重复使用或者重复释放,进而导致业务故障。

  • 对于幂等类型的问题,通常的手段是引入幂等字段进行防重放攻击。对于分布式事务框架中的幂等问题,同样可以祭出这一利器。

  • 幂等记录的插入时机是参与者的Try方法,此时的分支事务状态会被初始化为INIT。然后当二阶段的Confirm/Cancel执行时会将其状态置为CONFIRMED/ROLLBACKED。

  • 当TC重复调用二阶段接口时,参与者会先获取事务状态控制表的对应记录查看其事务状态。如果状态已经为CONFIRMED/ROLLBACKED,那么表示参与者已经处理完其分内之事,不需要再次执行,可以直接返回幂等成功的结果给TC,帮助其推进分布式事务。

空回滚

  • 当没有调用参与方Try方法的情况下,就调用了二阶段的Cancel方法,Cancel方法需要有办法识别出此时Try有没有执行。如果Try还没执行,表示这个Cancel操作是无效的,即本次Cancel属于空回滚;如果Try已经执行,那么执行的是正常的回滚逻辑。

  • 要应对空回滚的问题,就需要让参与者在二阶段的Cancel方法中有办法识别到一阶段的Try是否已经执行。很显然,可以继续利用事务状态控制表来实现这个功能。

  • 当Try方法被成功执行后,会插入一条记录,标识该分支事务处于INIT状态。所以后续当二阶段的Cancel方法被调用时,可以通过查询控制表的对应记录进行判断。如果记录存在且状态为INIT,就表示一阶段已成功执行,可以正常执行回滚操作,释放预留的资源;如果记录不存在则表示一阶段未执行,本次为空回滚,不释放任何资源。

资源悬挂

  • 问题:TC回滚事务调用二阶段完成空回滚后,一阶段执行成功
  • 解决:事务状态控制记录作为控制手段,二阶段发现无记录时插入记录,一阶段执行时检查记录是否存在

SAGA - 事务

  • 思路:SAGA 事务相比于TCC事务,就是将大事务拆分成每个系统服务小事务,然后发生错误,不采用TCC的回滚,而是采取给予补偿,所以也不需要冻结。
    正向补偿:如果某个子事务提交失败,则一直对该事务进行重试,直至成功为止。
    反向补偿:如果某个子事务提交失败,则对该子事务及其之前所有的子事务进行补偿操作。
    总的来说SAGA相较于TCC,就是补偿代替回滚。

  • Saga 模式是长事务解决方案,适用于业务流程长且需要保证事务最终一致性的业务系统,Saga 模式一阶段就会提交本地事务,无锁,长流程情况下可以保证性能,多用于渠道层、集成层业务系统。事务参与者可能是其它公司的服务或者是遗留系统的服务,无法进行改造和提供 TCC 要求的接口,也可以使用 Saga 模式。

AT - 事务

AT事务参照2PC算法。AT事务的思路就是快照思路。
业务数据提交时,自动拦截所有的SQL,分别保存SQL对数据修改前后的快照。
如果分布式事务成功,那么直接删除快照中的记录。
如果分布式事务失败,那么事务回滚,根据日志数据自动产生用于补偿的逆向SQL。

你可能感兴趣的:(微服务,java,分布式)