1.什么是XA?
XA 规范 是 X/Open 组织定义的分布式事务处理(DTP,Distributed Transaction Processing) 标准。
典型的二段式事务解决方案
在 Seata 定义的分布式事务框架内,利⽤事务资源(数据库、消息服务等)对 XA 协议的⽀持, 以 XA 协议的机制来管理分⽀事务的⼀种事务模式。
XA方案作为业界的一种分布式事务的标准,主流的关系型数据库,Mysql、Oracle、PgSql等对他都有良好的支持。所以在我们开发的时候,只要是这个数据库支持XA方案,Seata就可以支持这个数据库并提供相应的服务。
2.Seata体系三个角色
事务管理器(TM):划分事务的边界范围,决定什么时候全局提交/回滚(司令官)
事务协调者(TC):负责通知命令的中间件Seata-Server(传令官)
资源管理器(RM):做具体事儿的工具人(大头兵)
3.XA的执行过程
3.1.场景说明
如下是一个商城项目:BussinessService表示商城入口是TM服务,OrderService表示订单服务,AccountService表示库存服务是RM服务。
3.2.阶段1
(1)TM在启动的时候,创建订单之前,会自动的向TC发起一个请求注册全局事务,说明要开始干活了
(2)TM 远程调用RM端的OrderServer,完成订单创建的操作,OrderServer在进行数据写入数据库前会自动的和TC完成一个分支事务,随后执行insert into语句,但是并没有实际的提交
(3)TM 远程调用RM端的AccountService完成扣减余额的操作,AccountService在进行数据更新数据库前会自动的和TC完成一个分支事务,随后执行update语句,但是并没有实际的提交
(4)当TM远程调用两个方法都处理成功之后,阶段1就处理完成了,开始进入第二个阶段
3.3.阶段2
分支一:两个都处理成功
(1)TM向TC下达全局提交指令,然后TC主动的向两个RM端推送commit命令
(2)RM端收到commit命令之后,完成刚才的sql语句提交,这样就保证了整体的一致性
分支二:存在处理失败的情况
(1)TM向TC下达全局回滚指令,然后TC主动的向两个RM推送rollback命令
(2)RM端收到rollback命令之后,回滚刚才的sql语句
4.代码示意
TM端:BussinessService规定事务边界
public class BussinessService { @GlobalTransactional //Seata独有的注解,表示开启全局事务,自动向TC下达指令 public void purchase(String userId, String commodityCode, int orderCount,int money, boolean rollback) { //账户余额减少 String result = accountFeignClient.reduce(userId, money); //向Order服务调⽤创建订单 result = orderFeignClient.create(userId, commodityCode, orderCount); } }
purchase方法执行成功,自动进入XA的二阶段,向TC下达全局提交的命令,中间执行失败,则向TC下达全局回滚的命令。这个过程完全由@GlobalTransactional注解完成,无需要额外控制
RM1:OrderService完成创建订单
对原有数据源进行扩展:Seata通过DataSourceProxyXA包装原⽣DataSource⾃动实现全局事务控制,包括分支事务的注册以及控制分支事务的提交或者回滚
@Configuration public class OrderXADataSourceConfiguration { @Bean @ConfigurationProperties(prefix = "spring.datasource") public DruidDataSource druidDataSource() { return new DruidDataSource(); } @Bean("dataSourceProxy") public DataSource dataSource(DruidDataSource druidDataSource) { // DataSourceProxy for AT mode // return new DataSourceProxy(druidDataSource); // DataSourceProxyXA for XA mode return new DataSourceProxyXA(druidDataSource); } }
OrderService实现业务⽅法:使用标准的声明式事务@Transactional,描述业务方法,执行insert语句就可以了
@Service public class OrderService { @Autowired private JdbcTemplate jdbcTemplate; @Transactional public void create(String userId, String commodityCode, Integer count) { int orderMoney = count * 100; // ⽣成订单 jdbcTemplate.update("insert order_tbl(user_id,commodity_code,count,money) values(?,?,?,?)", new Object[] {userId, commodityCode, count, orderMoney}); } }
RM2:AccountService完成账户余额扣减
配置部分和RM1一致
@Configuration public class AccountXADataSourceConfiguration { @Bean @ConfigurationProperties(prefix = "spring.datasource") public DruidDataSource druidDataSource() { return new DruidDataSource(); } @Bean("dataSourceProxy") public DataSource dataSource(DruidDataSource druidDataSource) { // DataSourceProxy for AT mode // return new DataSourceProxy(druidDataSource); // DataSourceProxyXA for XA mode return new DataSourceProxyXA(druidDataSource); } }
Transactional实现业务⽅法
@Service public class AccountService { @Autowired private JdbcTemplate jdbcTemplate; @Transactional public void reduce(String userId, int money) { //减少余额 jdbcTemplate.update("update account_tbl set money = money - ? where user_id = ?", new Object[] {money, userId}); } }
5.XA的优势
基于CP设计,强⼀致性,不会出现状态不⼀致
主流关系数据库都对XA有⽀持,数据业界标准
⼆段提交实现简单粗暴,成熟稳定
6.XA的劣势
执⾏效率低,所有操作都是基于数据库事务处理,数据没有被提交前会被hang住阻塞,⾼并发系统禁⽤
协议阻塞,在我们进行数据处理的时候,底层建立的是一个长链接,长连接建立后,在收到 XA commit 或 XA rollback 前必须阻塞等待,对网络连接利用率也是一个问题,如果⼀个参与全局事务的资源 “失联” ,其他RM资源会hang住,等待超时回滚,导致性能进⼀步下降
强制要求数据库特性⽀持XA,不⽀持的就没法⽤
7.适⽤场景
⾦融⾏业,并发量不⼤,但数据很重要的项⽬
其实XA适⽤于绝⼤多数企业及应⽤,哪里有那么多高并发事务写入场景。
转载请注明:西门飞冰的博客 » 分布式事务之XA方案(Seata实现)