分布式事务实现方式

133 阅读7分钟

前言

在分布式系统中,单机事务以及由于网络、系统稳定性等各种因素无法保证各系统间的数据一致性。所以在分布式系统中需要使用分布式事务来保证各个系统间的数据一致性。下面将对常见的分布式事务实现方式做一些介绍。

XA协议

XA协议 是由 X/Open 组织提出的分布式事务处理规范,主要定义了事务管理器 TM 和局部资源管理器 RM 之间的接口。目前主流的数据库,比如 oracle、mysql 都是支持 XA 协议的。

事务管理器作为一个全局的调度者,负责对各个本地资源管理器统一号令提交或者回滚事务。二阶提交协议(2PC)和三阶提交协议(3PC)就是根据此协议衍生出来。

分布式事务实现方式对比

两段提交(2PC) 

两段提交(2PC) 顾名思义就是要进行两个阶段的提交:第一阶段,准备阶段(投票阶段) ; 第二阶段,提交阶段(执行阶段)。

第一阶段: 事务管理器给每一个资源管理器发送Prepare消息,资源管理器执行本地数据脚本但不提交事务,资源管理器在执行后会给事务管理器响应执行结果。

第二阶段: 事务管理器如果收到了资源管理器的失败消息或者超时,直接给每个资源管理器发送回滚(Rollback)消息;否则,发送提交(Commit)消息;资源管理器根据事务管理器的指令执行提交或者回滚操作,并释放所有事务处理过程中被占用的资源,显然2PC做到了所有操作要么全部成功、要么全部失败。

缺点
二阶段提交看似能够提供原子性的操作,但它存在着严重的缺陷

  • 网络抖动导致的数据不一致: 第二阶段中事务管理器向资源管理器发送commit命令之后,一旦此时发生网络抖动,导致一部分资源管理器接收到了commit请求并执行,可其他未接到commit请求的资源管理器无法执行事务提交。进而导致整个分布式系统出现了数据不一致。
  • 超时导致的同步阻塞问题: 2PC中的所有的资源管理器节点都为事务阻塞型,当某一个资源管理器节点出现通信超时,其余资源管理器都会被动阻塞占用资源不能释放。
  • 单点故障的风险: 由于严重的依赖事务管理器,一旦事务管理器发生故障,而此时资源管理器还都处于锁定资源的状态,无法完成事务commit操作。虽然事务管理器出现故障后,会重新选举一个事务管理器,可无法解决因前一个事务管理器宕机导致的资源管理器处于阻塞状态的问题。

image.png

三段提交(3PC)

三段提交(3PC)是对两段提交(2PC)的一种升级优化,3PC在2PC的第一阶段和第二阶段中插入一个准备阶段。保证了在最后提交阶段之前,各参与者节点的状态都一致。同时事务管理器和资源管理器中都引入超时机制,当资源管理器各种原因未收到事务管理器的commit请求后,会对本地事务进commit,不会一直阻塞等待,解决了2PC协调者(事务管理器)的单点故障问题(超时机制),但3PC 还是没能从根本上解决数据一致性的问题。

3PC 的三个阶段分别是CanCommit、PreCommit、DoCommit:

  1. CanCommit:事务管理器向所有资源管理器发送CanCommit命令,询问是否可以执行事务提交操作。如果全部响应YES则进入下一个阶段。
  2. PreCommit:事务管理器向所有资源管理器发送PreCommit命令,询问是否可以进行事务的预提交操作,资源管理器接收到PreCommit请求后,如资源管理器成功的执行了事务操作,则返回Yes响应,进入最终commit阶段。一旦资源管理器中有向事务管理器发送了No响应,或因网络造成超时,事务管理器没有接到资源管理器的响应,事务管理器向所有资源管理器发送abort请求,资源管理器接受abort命令执行事务的中断。
  3. DoCommit: 在前两个阶段中所有资源管理器的响应反馈均是YES后,事务管理器向资源管理器发送DoCommit命令正式提交事务,如果事务管理器没有接收到资源管理器发送的ACK响应,会向所有资源管理器发送abort请求命令,执行事务的中断。

image.png

TCC

TCC(Try-Confirm-Cancel)又被称补偿事务,TCC与2PC的思想很相似,事务处理流程也很相似,但2PC是应用于在DB层面,TCC则可以理解为在应用层面的2PC,是需要我们编写业务逻辑来实现。

Try阶段:
尝试执行,完成所有业务检查(一致性),预留必须业务资源(准隔离性)
Confirm阶段:
确认执行真正执行业务,不作任何业务检查,只使用Try阶段预留的业务资源,Confirm操作满足幂等性。要求具备幂等设计,Confirm失败后需要进行重试。
Cancel阶段:
取消执行,释放Try阶段预留的业务资源 ,Cancel操作满足幂等性, Cancel阶段的异常和Confirm阶段异常处理方案基本上一致。

TCC事务机制的优点:

  1. 解决了协调者单点:由主业务方发起并完成这个业务活动。业务活动管理器也变成多点,引入集群。
  2. 性能提升:由具体业务来实现控制资源,锁的粒度变小,不会锁定整个资源。Tcc对每个资源的操作都是一个本地事务,数据被lock的时间短,可扩展性好。
  3. 数据最终一致性:基于 Confirm 和 Cancel 的幂等性,保证事务最终完成确认或者取消,保证数据的一致性。

缺点:

  1. TCC 的 Try、Confirm 和 Cancel 操作功能要按具体业务来实现,业务耦合度较高,提高了开发成本。

image.png

可靠消息表

可靠消息表和 TCC 一样也是最终一致性。在高并发场景下,强一致性会非常影响系统的性能。所以在互联网系统中,常用 TCC 或者 可靠消息表来做分布式事务。但两者的使用场景上有一定的差别:

如果一个业务操作需要预先保证资源足够的情况下才能进行下一步操作时,就需要使用 TCC(如:电商场景订单系统需要预先扣减库存,扣减库存成功才能生成订单)。

如果一个业务操作不需要预先进行资源处理操作,则可以使用可靠消息表来保证(如:订单下单成功后,通知发货系统发货)。

可靠消息表保证了消息一定会投递成功,也就是保证消息至少会投递一次,下游系统一定能够收到消息。但是这种情况下需要下游系统保证幂等,多次投递的情况下保证不会出现重复消费的问题(一般基于业务上的唯一ID来控制)。

可靠消息表基于 MySQL + Job 的方式来实现。投递消息前将消息写入到消息表中,并将消息状态置为status = 0 (待投递)。定时任务扫描待投递的消息,将消息捞出来后投递到 MQ 中,投递成功后将消息状态置为status = 1 (投递成功)。如果多次投递失败,如投递 10 次+后仍失败,需要将消息状态改为 status = 2(投递失败),目的是防止待投递消息积压对系统产生较大压力,此时需要告警到钉钉或企微等平台人工介入处理。

可靠消息表.png