分布式事务原理

本地事务与分布式事务

事务的概念对每个程序员来说都不陌生,它是数据库提供的一种数据一致性保护机制,确保对某一批数据的操作要么全部成功,要么全部撤销。事务机制的便利性也减轻了不少日常开发工作量,我们不需要记录每一个业务场景下改动过什么数据,也不用在执行复杂计算逻辑前把所有可能会用到的资源都加锁,一旦事务执行过程遇到问题无法继续,简单的一个回滚指令即可让曾经发生的修改全部撤除。

经过长时间发展,事务机制的可靠性已经得到充分验证,至少我们不用担心数据库回滚过程会漏了哪条记录没有还原。事务的使用方式也越来越简化,例如Spring框架下只需要一个简单的@Transactional注解就可以开启事务,如下:

    @Transactional
    public String addBills(Bill bill) {
    	//事务内容
    	......
    }

不过使用的方法越简单,就越容易让人忘记其中存在的坑。笔者初次使用Spring框架时就在想,既然这么容易就能开启事务,为什么不在所有写数据库的业务方法上都加事务注解呢?于是过不其然,我就掉坑里了。

一天一个下游系统的同事反馈说,“你传的部分数据校验不了,是不是没有检查完整性就发过来了”。我当时的表情:
否认三连

不过说归说,出现问题还是要主动处理。按照对方提供的ID号到系统一查,提示无此数据,一度怀疑对方在忽悠自己,正准备甩锅之际,眼光注意到了方法的事务注解,突然意识到了问题所在。

在这里插入图片描述

上面是程序简化的处理流程

  1. 数据检查并格式化;
  2. 保存数据到本地数据库;
  3. 推送数据给A系统;
  4. 推送数据给B系统;
  5. 参数校验不通过或数据推送不成功,抛异常触发回滚。

通常上面的逻辑能够正常运行。不过考虑第5步骤,假设推送B系统的时候遇到网络故障导致失败,事务将回滚,同时撤销第一步写入的数据。而第3步推送给A系统的数据由于不在本地数据库,事务并不会自动帮程序追回,于是出现了上面的现象:外部系统收到了一条并不存在的数据。

传统事务强依赖于数据库实现,例如mysql使用redo log/undo log记录事务操作日志,mvcc机制用于并发事务下的数据隔离,因此存在一个天然的缺陷,即无法跨实例实现事务控制。在软件架构越来越复杂的今天,单实例数据库显然无法支撑业务需求,于是便有了分布式事务的出现。

分布式事务将本地以及远程的数据操作纳入到同一个逻辑事务的管理过程,使原本只能操作单个数据库的事务能力扩展到能够支持跨网络、甚至跨不同系统平台的环境中。最终实现的效果就是,开发业务功能时不用再考虑底层数据是否分布在不同服务器上,减少不必要的数据流程设计。

分布式事务实现机制

与本地事务相比,分布式事务的一个特点是不再单纯依赖于数据库实现,更多是通过中间件层面的方式来提供解决方案。以下是几种常见的分布式事务实现机制:

一、DTP模型

Open Group是一个由众多商业公司共同组成的技术标准制定和认证组织,其中该组织定义了一套分布式事务模型基准DTP
在DTP模型中,分布式事务由四个关键模块构成:
DTP模型结构
AP:应用程序。即分布式事务的请求方,包含了一系列对数据库或其他类型资源的修改操作。
TM:事务管理器,全局事务的管理节点,控制事务的发起,提交以及回滚。
RM:资源管理器。负责控制对公共资源的访问。
CRM:通讯管理器。DTP模型中,数据管理的单元由一个AP、一个TM以及多个RM 组合而成,称为实例,多个实例打包在一起构成TM Domain。CRM则负责TM Domain之间的通信。

资源管理器(RM)通常就是我们的数据库;应用程序(AP)则是我们所开发的业务代码逻辑;通讯管理器(CRM)常见的实现为消息中间件;而事务管理器(TM)则是新引入的全局事务管理模块。因此在DTP模型中,实现分布式事务的关键是如何控制TM与RM之间的交互,即XA协议

XA协议默认使用两阶段提交(2PC)的方式实现不同RM之间资源的提交和回滚。2PC技术并不是XA规范中提出,最早在数据库领域中已经实现,XA将这一技术规范引入到了中间件层面,并进行了相应优化。

2PC将原本数据库的事务流程的拆分为准备阶段(Prepare Phase)、提交阶段(Commit Phase)两部分,因此称为两阶段提交。其处理过程如下:

  • 事务管理器向所有参与者发送prepare请求,并等待各参与者反馈;
  • 参与者收到prepare消息时,按正常流程执行事务操作,记录redo log / undo log,但在事务执行完毕后不执行 commit / rollback 操作,而是向事务管理器发送本次事务处理结果;
  • 如果所有参与者事务都执行成功,事务管理器则向所有参与者发送commit消息,通知各数据库完成最终数据入库。
  • 如果任意一个参与者prepare阶段执行失败或超时未收到回复,事务管理器则向参与者发送rollback消息,让各参与者撤销所有修改操作。
    事务提交事务回滚

2PC方案引入了一个预处理的步骤,每个参与数据库在预处理完数据后进入挂起状态,等待事务管理器发送提交或回滚消息才完成最后的入库,因此存在几个缺点:

  • 事务挂起阶段各个子数据库依然会持有资源锁,如果某一个参与者长时间没有反馈执行结果,会导致其它参与者的资源也得不到释放,性能较差;
  • 如果第一阶段执行时事务管理器发送故障,所有参与者会一致处于阻塞状态,存在单点故障风险;
  • Commit阶段如果事务管理器故障或网络异常导致部分参与者没有收到commit消息,会出现数据不一致问题。

为了解决2PC方案的单点故障问题,后续出现了3PC协议,引入超时机制并将Commit阶段拆分成了CanCommit、PreCommit、DoCommit等步骤,不过锁占用的问题依然存在。

二、TCC事务补偿

TCC(Try Confirm Cancel )又称为事务补偿,将事务操作拆分成了下面三个部分

Try:业务检查与资源预留;
Confirm:执行业务操作;
Cancel:取消业务操作,释放预留资源;

从流程上看,TCC与2PC机制相似,也是分 预处理 -> 提交 / 回滚 两阶段。实际上TCC可以认为是2PC的一种,其中最大的区别是2PC在数据库层面实现资源操作,TCC则是在应用层面,也就是说需要开发人员自己来实现资源锁定、解锁、提交的逻辑。因此,与其说TCC是一种技术标准,更多是对程序开发的一种规范要求。

TCC事务流程与DTM模型一样,TCC的实现也需要有事务管理器TM作为全局事务管理者,它的执行过程如下:

  1. 事务发起者向TM注册,开启全局事务;
  2. 调用 Service A Try接口前,向TM注册分支事务;
  3. 调用 Service A 的Try接口;
  4. 重复2, 3步,依次注册并调用各个参与者服务的Try接口;
  5. 事务发起者向TM发送Confirm / Cancel请求,由TM完成事务的提交或回滚。

同样,TCC也会存在和2PC相同的单点故障问题,不过TCC没有使用3PC方式,而是使用了更为直接的方法:不断重试,直到超过上限要求人工介入。因此,TCC机制有个前提是Confirm和Cancel接口的实现必须是幂等操作。

TCC把原本由数据库负责的资源锁定、正向/逆向数据处理从数据库层提取到了应用层,因此应用可以自由定义数据操作的粒度,降低资源冲突;同时数据存储也不受限于数据库,可以是任意数据对象,有非常高的灵活性。

而与之对应的,TCC缺点在于对应用侵入性非常高,所有业务服务都必须依照规范实现Try、Confirm、Cancel接口;由于需要开发人员自行设计实现资源回滚策略,任意一个参与者服务的接口若存在Bug都会导致数据不一致,对代码质量有较高要求;同时由于接口必须满足幂等性,开发难度也会有所提升。

三、总结

除了上面提到2PC、TCC等分布式事务机制,还有基于消息队列实现分布式事务的方案,不过其本质是基于消息中间件的两阶段提交,各个参与者的本地事务依然通过数据库进行处理。

回顾分布式事务原理,其核心就是将原本数据库事务的 执行 -> 提交 / 回滚 模式放大到了应用服务的层面。可以将各个应用服务想象成是单个SQL语句,通过引入全局事务管理器,对事务执行过程记录对应的“redo log”/“undo log”,并最终决定 Commit / Rollback 事务。

分布式事务技术和理念并不是最近才出现,不过目前依然没有看到大范围的使用。究其原因,分布式事务并不是问题的唯一解,大部分分布式事务的应用场景,通过合理的架构和代码设计一样能够达到数据一致的目标。

正如文章开头笔者所遇到的数据推送问题,除了使用分布式事务方案,还可以考虑将数据保存和推送过程拆开,通过定时任务轮询本地数据库,判断哪些数据尚未推送并进行处理;也可以让下游系统提供数据清理接口,在发生事务回滚时一并删除已经推送的记录。

由于分布式事务不是无侵入的解决方案,需要改造已有程序和代码后才能正常接入使用,对于许多已经上线并运行的系统来说成本过高。而一旦数据链路上存在任一无法纳入分布式事务的参与者,整个数据流的一致性就无法充分保障,实现效果也大打折扣。不过随着微服务化的流行,关于分布式事务的探讨也将不断深入,当我们能够像本地事务一样通过注解就可以自由开启分布事务时,我的表情包应该还能继续一用。

作者:阮伟聪

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值