详细请参考官方文档:分布式事务 :: ShardingSphere
首先确定好自身引入的shradingjdbc版本号,因为我用的它的starter版本,所以没用这种core包的版本,所以yml配置还有分布式事务的配置也不尽相同,此处我以starter版本为阐述。
org.apache.shardingsphere
sharding-jdbc-spring-boot-starter
4.0.0
org.apache.shardingsphere
sharding-jdbc-core
${latest.release.version}
此外支持跨库事务必须配置对应的事务管理器的bean
package com.mode.technology.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import javax.sql.DataSource;
@Configuration
@EnableTransactionManagement
public class TransactionConfiguration {
@Bean
public PlatformTransactionManager txManager(final DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
}
核心概念
两阶段事务-XA
两阶段事务提交采用的是X/OPEN组织所定义的DTP模型,通过抽象出来的AP, TM, RM的概念可以保证事务的强一致性。 其中TM和RM间采用XA的协议进行双向通信。 与传统的本地事务相比,XA事务增加了prepare阶段,数据库除了被动接受提交指令外,还可以反向通知调用方事务是否可以被提交。 因此TM可以收集所有分支事务的prepare结果,最后进行原子的提交,保证事务的强一致性。
Java通过定义JTA接口实现了XA的模型,JTA接口里的ResourceManager需要数据库厂商提供XA的驱动实现,而TransactionManager则需要事务管理器的厂商实现,传统的事务管理器需要同应用服务器绑定,因此使用的成本很高。 而嵌入式的事务管器可以以jar包的形式提供服务,同ShardingSphere集成后,可保证分片后跨库事务强一致性。
通常,只有使用了事务管理器厂商所提供的XA事务连接池,才能支持XA的事务。ShardingSphere整合XA事务时,分离了XA事务管理和连接池管理,这样接入XA时,可以做到对业务的零侵入。
实现原理查看官网文档:
https://shardingsphere.apache.org/document/legacy/4.x/document/cn/features/transaction/principle/2pc-xa-transaction/
Saga事务
Saga这个概念来源于三十多年前的一篇数据库论文Sagas ,一个Saga事务是一个有多个短时事务组成的长时的事务。 在分布式事务场景下,我们把一个Saga分布式事务看做是一个由多个本地事务组成的事务,每个本地事务都有一个与之对应的补偿事务。在Saga事务的执行过程中,如果某一步执行出现异常,Saga事务会被终止,同时会调用对应的补偿事务完成相关的恢复操作,这样保证Saga相关的本地事务要么都是执行成功,要么通过补偿恢复成为事务执行之前的状态。
自动反向补偿
Saga定义了一个事务中的每个子事务都有一个与之对应的反向补偿操作。由Saga事务管理器根据程序执行结果生成一张有向无环图,并在需要执行回滚操作时,根据该图依次按照相反的顺序调用反向补偿操作。Saga事务管理器只用于控制何时重试,何时补偿,并不负责补偿的内容,补偿的具体操作需要由开发者自行提供。
ShardingSphere采用反向SQL技术,将对数据库进行更新操作的SQL自动生成反向SQL,并交由saga-actuator执行,使用方则无需再关注如何实现补偿方法,将柔性事务管理器的应用范畴成功的定位回了事务的本源——数据库层面。
Seata柔性事务
Seata是阿里集团和蚂蚁金服联合打造的分布式事务框架,截止到0.5.x版本包含了AT事务和TCC事务。其中AT事务的目标是在微服务架构下,提供增量的事务ACID语意,让用户像使用本地事务一样,使用分布式事务,核心理念同ShardingSphere一脉相承。
Seata AT事务模型
Seata柔性事务
Seata是阿里集团和蚂蚁金服联合打造的分布式事务框架,截止到0.5.x版本包含了AT事务和TCC事务。其中AT事务的目标是在微服务架构下,提供增量的事务ACID语意,让用户像使用本地事务一样,使用分布式事务,核心理念同ShardingSphere一脉相承。
Seata AT事务模型
Seata AT事务模型包含TM(事务管理器),RM(资源管理器),TC(事务协调器)。其中TC是一个独立的服务需要单独部署,TM和RM以jar包的方式同业务应用部署在一起,它们同TC建立长连接,在整个事务生命周期内,保持RPC通信。 其中全局事务的发起方作为TM,全局事务的参与者作为RM ; TM负责全局事务的begin和commit/rollback,RM负责分支事务的执行结果上报,并且通过TC的协调进行commit/rollback。
支持功能
本地事务
功能
- 完全支持非跨库事务,例如:仅分表,或分库但是路由的结果在单库中。
- 完全支持因逻辑异常导致的跨库事务。例如:同一事务中,跨两个库更新。更新完毕后,抛出空指针,则两个库的内容都能回滚。
- 不支持因网络、硬件异常导致的跨库事务。例如:同一事务中,跨两个库更新,更新完毕后、未提交之前,第一个库宕机,则只有第二个库数据提交。
支持情况
- Sharding-JDBC和Sharding-Proxy原生支持本地事务(无需引入其它依赖)
测试新增
我这三个sql会根据zone字段落到不同的库,执行此接口发现数据库没有新增数据,说明事务确实回滚了
@ApiOperation(value = "测试分库分表插入-分布式事务-本地事务")
@GetMapping("/addTransactional")
@Transactional(rollbackFor = Exception.class)
public Tip addTransactional() {
TOrder orderBJ = new TOrder();
orderBJ.setUserId(100);
orderBJ.setZone("bj");
torderDao.insertSelective(orderBJ);
TOrder orderSH = new TOrder();
orderSH.setUserId(100);
orderSH.setZone("sh");
torderDao.insertSelective(orderSH);
TOrder orderSZ = new TOrder();
orderSZ.setUserId(100);
orderSZ.setZone("sz");
torderDao.insertSelective(orderSZ);
int a= 1/0;
return TipUtil.success();
}
二阶段事务-XA
ShardingSphere默认的XA事务管理器为Atomikos,在项目的logs目录中会生成xa_tx.log, 这是XA崩溃恢复时所需的日志,请勿删除。也可以通过在项目的classpath中添加jta.properties来定制化Atomikos配置项。具体的配置规则请参考Atomikos的官方文档。
功能
- 支持数据分片后的跨库XA事务
- 两阶段提交保证操作的原子性和数据的强一致性
- 服务宕机重启后,提交/回滚中的事务可自动恢复
- SPI机制整合主流的XA事务管理器,默认Atomikos,可以选择使用Narayana和Bitronix
- 同时支持XA和非XA的连接池
- 提供spring-boot和namespace的接入端
不支持项
- 服务宕机后,在其它机器上恢复提交/回滚中的数据
需要引入对应的pom包
org.apache.shardingsphere
sharding-transaction-xa-core
4.0.0
两阶段提交主要的原理是以上面的请求业务为例,当整个业务完成了之后只是第一阶段提交,在第二阶段提交之前会检查其他所有事务是否已经提交,如果前面出现了错误或是没有提交,那么第二阶段就不会提交,而是直接rollback操作,这样所有的事务都会做Rollback操作。上面的案例也可以通过二阶段xa事务来管理,改写下,相比第一种它对于容灾,务宕机重启后,它依然会提交/回滚中事务,可自动恢复
@ApiOperation(value = "测试分库分表插入-分布式事务-xa事务")
@GetMapping("/addTransactional")
@Transactional(rollbackFor = Exception.class)
@ShardingTransactionType(value = TransactionType.XA)
public Tip addTransactional() {
TOrder orderBJ = new TOrder();
orderBJ.setUserId(100);
orderBJ.setZone("bj");
torderDao.insertSelective(orderBJ);
TOrder orderSH = new TOrder();
orderSH.setUserId(100);
orderSH.setZone("sh");
torderDao.insertSelective(orderSH);
TOrder orderSZ = new TOrder();
orderSZ.setUserId(100);
orderSZ.setZone("sz");
torderDao.insertSelective(orderSZ);
int a= 1/0;
return TipUtil.success();
}
上述基于XA协议的全局事务,是属于强一致性事务,因为在全局事务中,只要有任何一个RM出现异常,都会导致全局事务回滚。同时,本地事务在Prepare阶段锁定资源时,如果有其他事务要要修改相同的数据,就必须要等待前面的事务完成,这本身是无可厚非的设计,但是由于多个RM节点是跨网络,一旦出现网络延迟,就导致该事务一直占用资源使得整体性能下降。所以它不适用于对并发有要求的情况
另外,在XA COMMIT阶段,如果其中一个RM因为网络超时没有收到数据提交的指令,会导致数据不一致,为了解决这个问题,很多开源分布式事务框架都会提供重试机制来保证数据一致性。
柔性事务-saga
- 完全支持跨库事务
- 支持失败SQL重试及最大努力送达
- 支持反向SQL、自动生成更新快照以及自动补偿
- 默认使用关系型数据库进行快照及事务日志的持久化,支持使用SPI的方式加载其他类型的持久化
不支持项
- 暂不支持资源隔离
- 暂不支持服务宕机后,自动恢复提交中的commit和rollback
支持情况
ShardingSphere的柔性事务已通过第三方SPI实现Saga事务,Saga引擎使用Servicecomb-Saga。
注意
- 反向SQL需要主键,请确保在表结构中定义主键。
- 对于INSERT语句, 需要在SQL中显示插入主键值,如INSERT INTO ${table_name} (id, value, ...) VALUES (11111, '', ....) (其中id为表主键)。
- 若需要自动生成主键,可使用ShardingSphere的分布式主键。
实现原理官网参考:
https://shardingsphere.apache.org/document/legacy/4.x/document/cn/features/transaction/principle/base-transaction-saga/
柔性事务-seta
功能
- 完全支持跨库分布式事务
- 支持RC隔离级别
- 通过undo快照进行事务回滚
- 支持服务宕机后的,自动恢复提交中的事务
依赖
- 需要额外部署Seata-server服务进行分支事务的协调
待优化项
- ShardingSphere和Seata会对SQL进行重复解析
实现原理官网参考:
https://shardingsphere.apache.org/document/legacy/4.x/document/cn/features/transaction/principle/base-transaction-seata/
结尾
对于sega和seta这两种柔性事务的处理,我目前工作中还没遇到过,如果真的要用我肯定会优先看下阿里的seta框架,将它集成到我们项目中。