当你觉得一切都为时已晚的时候,其实恰恰是最早的时候。
闲言碎语
在前面的章节我们讨论分布式事务的基础理论部分,我们也说明了单机事务是如何管理的。同时我们阐述了分布式事务产生的背景,及分布式事务实践相关基础理论,并分析了 "CAP理论" 以及分布式系统实践中"CAP"该如何进行取舍。
其实软件行业前辈们在分布式系统设计中,基于CAP理论基础进行了反复的实践,同时又在CAP理论基础之上总结延伸出了"BASE理论",本篇文章我们主要全篇幅讲分布事务的解决方案,但如果你对基础理论有兴趣可以阅读我写的上一篇文章:微服务分布式事务解决方案实战(理论基础篇)
牛顿说:“如果我看的比别人远,那是因为我站在巨人的肩膀上。”
你遇到过的问题前辈们也遇到过,而且在过往的系统实践中已经摸索和总结了一套完整可靠、可落地的解决方案,并会有配套的技术框架支撑。当你遇到同类问题时,不要迷茫和害怕。因为行业中大部分问题都有成熟的解决方案了,当然也包括今天我们讨论的分布式事务问题。
XA事务协议
在讨论具体解决技术方案前我们先要讲一下“XA事务协议”,它是由X/Open组织提出的分布式事务的规范,记住它是一套规范。
该规范主要定义了全局事务管理器(TM)、资源管理器(RM)之间的接口。主流的关系型数据库产品都是实现了XA接口的。它主要核心内容如下:
-
XA接口是双向的系统接口,在事务管理器以及一个或多个资源管理器之间形成通信桥梁。
-
XA之所以需要引入事务管理器是因为,在分布 式系统中,从理论上讲两台机器理论上无法达到一致的状态,需要引入一个单点进行协调。
-
由全局事务管理器管理和协调的事务,可以跨越多个资源(如数据库或JMS队列)和进程。全局事务管理器一般使用 XA二阶段提交协议与数据库进行交互。
其中XA分布式事务协议包含二阶段提交(2PC),三阶段提交(3PC)两种实现。细心我们可以发现这两种实现方案在很多成熟的分布式框架中都有体现,比如我们熟知的Alibaba 开源的分布式事务框架Seata 和 LCN开源的分布式事务框架TX-LCN。
二阶段提交(2PC)
-
二阶段事务(正常提交)
-
二阶段事务(中断事务)
二阶段提交(2PC)指的就是事务“准备阶段” 和 “提交阶段”。我们对上图的内容核心步骤进行了总结提炼。
准备阶段
1、事务协调者,向所有事务参与者发送事务内容,并等待事务参与者回复
2、各个参与者开始“执行事务操作”,但此时并不会提交事务
3、如果参与者执行成功或失败,都会给“事务协调者”回复一个消息,点头YES摇头NO,告诉事务协调者我当前的执行状态。
提交阶段
1、事务协调者根据收集汇总的结果进行分析判断,最终会下达一个指令。如果全部是"YES"就会给各个参与者发送“commit”指令,告诉兄弟们,干吧。
2、如果“事务协调者”收到参与者的失败信息或超时信息就会给所有参与者发送回滚(rollback)指令,通知各个参与者进行事务回滚操作。
本小节思考题
当事务协调者收到各个参与者回复后,如果全部是"YES"就会给参与者下达“commit”指令。号召兄弟们干吧。参与者指令开始提交事务操作,但在执行过程某一个参与者发生异常未提交成功,但其他参与者正常提交了事务操作,那么此刻就会发生数据不一致的问题了,你就说烦不烦?
三阶段提交(3PC)
-
三阶段提交(正常提交)
-
三阶段提交(中断事务)
从上图三阶段提交(3PC)的核心流程图中我们发现,三阶段提交是在二阶段提交上的改进版本,主要是加入了超时机制。同时在协调者和参与者中都引入超时机制。
1、canCommit
2、preCommit
3、do Commit
三阶段将二阶段的准备阶段拆分为2个阶段,插入了一个preCommit阶段,以此来处理原先二阶段,参与者准备后,参与者发生崩溃或错误,导致参与者无法知晓是否提交或回滚的不确定状态所引起的延时问题。
2PC和3PC比较
优点:相比二阶段提交,三阶段提交降低了阻塞范围,在等待超时后协调者或参与者会中断事务。避免了协调者单点问题,阶段 3 中协调者出现问题时,参与者会继续提交事务。
缺点:数据不一致问题依然存在,当在参与者收到 preCommit 请求后等待 do commit 指令时,此时如果协调者请求中断事务,而协调者无法与参与者正常通信,会导致参与者继续提交事务,造成数据不一致。
分布式事务解决方案
单体架构
在单体的项目中,多个不同业务逻辑都是在同一个数据源中实现事务管理,是不存在分布式事务的问题,因为同一数据源的情况下都是采用事务管理器,相当于每个事务管理器对应一个数据源。所以在单库场景下是不存在分布式事务的。
单体架构(多数据源)
在单体的项目中,因为业务原因会集成多个不同的数据源,并且每个数据源中都有自己独立的事务管理器,互不影响,那么这时候也会存在多数据源事务管理问题,刚好某一个业务操作需要向2个数据源中写入数据,按照事务的一致性原则,要么全部写入成功,要么全部失败。
单体架构(多数据源)推荐解决方案:jta + Atomikos 集成非常简便,SpringBoot官方提供的Atomikos集成支持。
分布式架构&微服务架构
如上图业务场景:
1、用户选中某一件商品并添加到购物车中,用户确定后提交订单。
2、系统生成订单后调用支付微服务,支付成功后调用库存微服务"扣减库存",调用积分微服务"添加积分"。按照事务的原子性,要么全部成功,要么全部失败,不允许存在中间状态。
3、订单、积分、库存、交易 都拆分成一个个独立的微服务,并对应独立的数据库,形成数据库垮库操作。
从上面的业务场景我们可以发现"用户下单"操作就形成了垮库操作,我们简单的理解垮库事务就是分布式事务。现在行业中提供了非常多成熟的框架来解决分布式事务问题,只要框架集成完毕,你几乎不需要过多的关注事务执行流程,让你聚焦你的业务实现,当然TCC 模式除外哈,因为用TCC模式代码处理还是比较麻烦的。
我也对行业分布式事务框架进行实践,并进行了部分汇总,我做成了一个思维导图,这样你就能很清晰的看清脉络(所见即所得)。后面有工作之余有时间,我会单独对目前非常流程的分布式框架Seata 和 TX-LCN 进行实战讲解,关注公众号不迷路。
从上面思维导图我们看到"TCC" 这个关键字高频率出现。Nice 目前开源主流分布式框架都对它TCC 有较好的支持,所以我也在文章末位进行简短介绍一下。
分布式事务TCC模式
TCC方案是将事务分成了三个阶段,分别是try、commit、callback阶段。其和两阶段提交有点类似,Try为第一阶段,Confirm - Cancel为第二阶段,是一种应用层面侵入业务的两阶段提交。
TCC业务流程:事务开始时,业务应用会向事务协调器注册启动事务。之后业务应用会调用所有服务的try接口,完成一阶段准备。之后事务协调器会根据try接口返回情况,决定调用confirm接口或者cancel接口。如果接口调用失败,会进行重试。
优点:TCC方案让应用自己定义数据库操作的粒度,使得降低锁冲突、提高吞吐量成为可能。
缺点:TCC方案也有不足之处,集中表现在以下两个方面:
1、对应用的侵入性强。业务逻辑的每个分支都需要实现try、confirm、cancel三个操作,应用侵入性较强,改造成本高。
2、实现难度较大。需要按照网络状态、系统故障等不同的失败原因实现不同的回滚策略。为了满足一致性的要求,confirm和cancel接口必须实现幂等。
上述原因导致TCC方案大多被研发实力较强、有迫切需求的大公司所采用。微服务倡导服务的轻量化、易部署,而TCC方案中很多事务的处理逻辑需要应用自己编码实现,复杂且开发量大。
下一篇我们准备针对分布式事务问题,给出 分布式事务框架 进行实践。识别图中的二维码,我们在这里一起成长。