阿里TXC分布式事务中间件

一、背景

在做玩法钱包项目时,其中一个场景,在使用账户资金创建红包时,先要预先判断账户资金是否充足,如果充足则冻结这笔资金,再创建红包,如果创建红包失败要取消冻结资金。这就涉及到了两个应用中红包库和资金库的跨应用跨库事务的问题。

本打算采取处理失败后使用metaq消息重试机制来做补偿,保证最终一致性。这种方式的好处是不引用新的中间件,对系统性能和吞吐量影响不大。但缺点也很明显,为了对数据进行补偿,针对不同场景需要新建不同的metaq重试任务,各种补丁代码也让代码逻辑变得复杂,且极端情况需要人工补偿。

针对以上问题去调研了集团的分布式事务中间件TXC,以下是对分布式事务及TXC的一些总结。

 


二、分布式事务常用解决方案


1. XA二阶段提交(2PC)


第一阶段是表决阶段,所有参与者都将本事务能否成功的信息反馈发给协调者;第二阶段是执行阶段,协调者根据所有参与者的反馈,通知所有参与者,步调一致地在所有分支上提交或者回滚。

两阶段提交方案应用非常广泛,它可以满足ACID,几乎所有收费的商业数据库都支持XA协议。

两阶段提交的致命缺点:XA在准备阶段不会提交本地事务,并且锁定资源时间长,在第一阶段上锁,第二阶段才能解锁,期间其他节点处于阻塞状态,这个对性能影响非常大,基本不适合应用于互联网海量流量的场景中。

 


2. TCC

TCC方案其实是两阶段提交的一种改进。其将整个业务逻辑的每个分支显式的分成了Try、Confirm、Cancel三个操作。Try部分完成业务的准备工作,confirm部分完成业务的提交,cancel部分完成事务的回滚。基本原理如下图所示。

image.png

事务开始时,业务应用会向事务协调器注册启动事务。之后业务应用会调用所有服务的try接口,完成一阶段准备。之后事务协调器会根据try接口返回情况,决定调用confirm接口或者cancel接口。如果接口调用失败,会进行重试。

TCC方案让应用自己定义数据库操作的粒度,使得降低锁冲突、提高吞吐量成为可能。TCC采用最终一致性的方案,让资源不再需要长时间上锁,极大的提高了吞吐量,但牺牲了ACID中的C和I。

 

当然TCC方案也有不足之处,表现在以下两个方面:

  • 对应用的侵入性强。业务逻辑的每个分支都需要实现try、confirm、cancel三个操作,应用侵入性较强,改造成本高。
  • 实现难度较大。需要按照网络状态、系统故障等不同的失败原因实现不同的回滚策略。为了满足一致性的要求,confirm和cancel接口必须实现幂等。

 


3. 基于消息的最终一致性方案

基于消息的最终一致性方案是通过消息中间件保证上、下游应用数据操作的。基本思路是将本地操作和发送消息放在一个事务中,保证本地操作和消息发送要么两者都成功或者都失败。下游应用向消息系统订阅该消息,收到消息后执行相应操作。

image.png

消息方案从本质上讲是将分布式事务转换为多个本地事务,然后依靠下游业务的重试机制达到最终一致性。基于消息的最终一致性方案性能比XA好很多,但同样对应用侵入性很高,应用需要进行大量业务改造,成本较高。

 


4. TXC

TXC(Taobao Transaction Constructor)是一个分布式事务中间件,它可以通过极少的代码侵入,实现分布式事务。

TXC的几个特性:

  • 高性能

相对XA,TXC的性能会强很多,基本能满足互联网海量流量的场景

  • 高可用

TXC支持同城容灾与两地三中心容灾,可以保证各种异常情况下的数据一致。

  • 最终一致性

TXC采用了最终一致性,因此部分牺牲了一致性和隔离性。在此前提下,TXC在性能、隔离性之间提供了数个平衡点,应用程序管理员可以通过配置不同的TXC策略来选择本应用所需要的平衡点。

  • 代码入侵少

TXC对业务低侵入,业务代码最少只需要添加一行注解(@TxcTransaction)声明事务即可。业务与事务分离,将微服务从事务中解放出来,微服务关注于业务本身,不再需要考虑反向接口、幂等、回滚策略等复杂问题,极大降低了微服务开发的难度与工作量。

  • 跨多应用的事务

TXC的事务可以在应用之间随着HSF服务调用被传播,使得跨多应用、跨多库的数据库操作可以被包含在同一个事务内。

 


三、TXC介绍


1. TXC的原理

image.png

TXC由三个组件组成:客户端(TXC-Client),资源管理器(RM),事务协调器(TXC-Server)。客户端与事务协调器间,资源管理器与事务协调器间都是通过TXC分布式事务协议进行通信。客户端负责界定事务边界,开启/提交/回滚全局事务,资源管理器负责管理资源,支持的资源包括:TDDL/DRDS,Oracle,MySQL,RDS,PgSQL,H2,MQ,MetaQ,Notify。事务协调器,也就是TXC服务器,负责协调整个事务过程,是分布式事务处理的大脑。

 

事务过程:

  1. TM 向 TC 申请开启一个全局事务,全局事务创建成功并生成一个全局唯一的 XID。
  2. XID 在微服务调用链路的上下文中传播。
  3. RM 向 TC 注册分支事务,将其纳入 XID 对应全局事务的管辖。
  4. TM 向 TC 发起针对 XID 的全局提交或回滚决议。
  5. TC 调度 XID 下管辖的全部分支事务完成提交或回滚请求。

 


2. TXC的3种模式


2.1 标准模式(AT模式)

  • 最主要的事务模式
  • 基于TDDL数据源,对SQL语句提供分布式事务支持
  • 对应用改造最小
  • 隔离级别默认读未提交。可设为读已提交
  • 适合TDDL分库分表、多TDDL数据源、跨进程多TDDL数据源等任何TDDL应用场景


2.2 自定义模式(MT模式)

  • 两阶段提交
  • 可根据自身业务自定义阶段行为
  • 更灵活、更多可能性,特殊场景自定义优化
  • 不依赖于TDDL,几乎满足任何事务场景
  • 由用户自己实现Prepare/Commit/Rollback方法,然后向TXC Server注册自定义的MT服务。


2.3 重试模式(RT模式)

  • 官方已不推荐

 


3. TXC的ACID权衡


3.1 原子性与一致性

在AT方式下,事务范围内所有分支的写SQL操作应该要么都执行,要么都不执行。


3.2 持久性

所有分支的状态都会被持久化,整个全局事务的状态也需要被持久化,以便在TXC Server或者client出故障之后可以回滚事务。


3.3 隔离性

TXC提供2种隔离配置策略:弱隔离性策略(缺省策略)、强隔离性策略。


(a) 弱隔离性策略

  • 写未提交
  • 如果记录在回滚前被别的事务并发修改了,回滚就无法进行。只能报警,需要手工订正。
  • 可能产生脏写
  • 适合对同一记录不太可能进行并发修改场景


(b) 强隔离性策略

  • 读未提交
  • 可避免脏写
  • 适合对同一记录可能频繁修改的场景

 


4. TXC事务的传播

  • 可随HSF服务传播
  • HSF默认是拒绝事务传播的,需显式的被打开
  • 多层级的传播要求每层服务都部署了TXC
  • 事务传播可以是异步的,但在事务提交前,应用必须保证所有异步操作都必须返回

 


四、TXC接入


1. 接入流程 


1.1 申请TXC资源

向TXC开发同学申请TXC资源,需提供aone应用名及TDDL的appname


1.2 配置代码依赖

在 POM 中引入:

  • txc-client
  • txc-resourcemanager
  • txc-datasource


1.3 配置TxcTransactionScaner

@Configuration
public class TxcConfig {
    @Bean
    public TxcTransactionScaner txcTransactionScaner() {
        //mode: 1:AT 2:MT 3:AT&MT 4:RT 5:AT&RT 6:MT&RT 7:AT&MT&RT
        TxcTransactionScaner ts = new TxcTransactionScaner("union-account", "union-account", 1);
        return ts;
    }
}

Copy

 


1.4 配置TxcTransaction注解

@TxcTransaction(appName = "createInstance", timeout = 6000)
public RightsInstanceCreateResult createRightsInstance(RightsInstanceCreateParam createParam) {
    return createRightsInstance(createParam);
}


 


1.5 建立TXC事务日志表

CREATE TABLE `txc_undo_log` (
  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',
  `gmt_create` datetime NOT NULL COMMENT '创建时间',
  `gmt_modified` datetime NOT NULL COMMENT '修改时间',
  `xid` varchar(100) NOT NULL COMMENT '全局事务ID',
  `branch_id` bigint NOT NULL COMMENT '分支事务ID',
  `rollback_info` longblob NOT NULL COMMENT 'LOG',
  `status` int NOT NULL COMMENT '状态',
  `server` varchar(32) NOT NULL COMMENT '分支所在的 TXC server IP',
  PRIMARY KEY (`id`),
  KEY `idx_unionkey` (`xid`,`branch_id`)
) DEFAULT CHARACTER SET=utf8 AUTO_INCREMENT=211225994 COMMENT='事务日志表';


 


1.6 支持HSF

@HSFProvider(serviceInterface = AssetAccountService.class, enableTXC = true)
public class AssetAccountServiceImpl implements AssetAccountService {
    ...
}

 


2. 接入注意事项

  • TxcTransaction注解只需在第一个应用入口方法处标注
  • HSF服务提供者需显式开启TXC
  • 整个链路中需要纳入TXC事务管理的数据库都需要建立TXC事务表,tddl应用只分库不分表(如分表拓扑:union_account_[0000-0015].txc_undo_log)
  • 每个应用都需配置TxcTransactionScaner

 


五、TXC压测


1. 案例

以这次玩法钱包为例,涉及两个应用:玩法应用、资产账户应用,每个应用分别有各自的数据库(TDDL),流程时序如下:

 

 


六、热点优化

在集群针对热点数据做压测时,QPS到达10就提升不上去了,配合TXC同学在预发和线上环境进行调试和压测,现将遇到的问题以及解决方法总结如下:

 


八、总结

TXC完美解决了我们项目中分布式事务的问题,整体来说,AT模式下接入成本还是比较低的,代码入侵少,改造容易。从性能上来说,接入后RT会少量增加,这也是必然的,处理事务的消耗,但这点牺牲可以完全接受,毕竟保证一致性才是第一位的。

评论区
Rick ©2018