单机服务事务提交回滚操作是需要拿到Connection对象,调用提交commit方法或者rollback方法回滚的,例如下面操作
Connection conn = DriverManager.getConnection(...);
try{
con.setAutoCommit(false);
Statement stmt = con.createStatement();
//1 or more queries or updates
con.commit();
}catch(Exception e){
con.rollback();
}finally{
con.close();
}
要想提交或者回滚,必须拥有Connection对象,然而在分布式环境,jvm都是不同的,自然就拿不到其他服务的Connection对象,所以在分布式环境,我们无法保证原子性。因此分布式事务就需要另寻出路。
2PC 即两阶段提交协议,是将整个事务流程分为两个阶段,准备阶段(Prepare phase)、提交阶段(commit phase)
与本地事务的区别就是 会加入一个事务协调者的角色,这个事务协调者控制整体的事务提交
在这里举个具体的例子
我们有两个服务:订单服务、库存服务
用户需要买东西,首先要创建订单,创建订单前需要去锁定库存,然后再去创建订单。
1. 事务协调者开启全局事务
2. 库存服务开启事务,向事务协调者注册一个分支事务
3. 库存服务锁定库存
4. 订单服务开启事务,向事务协调者注册一个分支事务
5. 订单服务创建订单
6. 事务协调者进行全局提交事务
7. 库存服务提交事务
8. 订单服务提交事务
9. 全局事务完成
在任何一个流程中异常,事务协调者都会发起全局回滚事务,这种方式,在全局事务完成前,Connection对象都不会释放,因为你释放了你就无法控制了,缺点很明显,如果订单服务需要处理很久,库存服务Connection对象都不会释放,一直占用着,这种是强原子性的很浪费资源
XA协议,是X/Open组织提出的跨异构技术实现2PC的接口标准。
使用XA协议首先前提就是,需要关系型数据库支持,目前主流数据库:
第一步开启XA事务
库存DB:
XA START 'xid'
UPDATE product SET num = num - 1 where id = 100
XA END 'xid'
订单DB:
XA START 'xid'
INSERT INTO order values(xxx)
XA END 'xid'
第二步,准备就绪,等待事务协调者同意我提交
库存DB:
XA PREPARE 'xid'
订单DB:
XA PREPARE 'xid'
全部提交
库存DB
XA COMMIT 'xid'
订单DB:
XA COMMIT 'xid'
XA协议JDK接口定义:javax.sql.XADataSource
seata框架支持XA协议,seataXA模式文档:https://seata.apache.org/zh-cn/docs/v1.6/dev/mode/xa-mode/
seata官方XA模式demo:
https://github.com/apache/incubator-seata-samples/tree/master/xa-sample/springboot-feign-seata-xa
三阶段提交协议(3PC)主要是为了解决两阶段提交协议的阻塞问题,2pc存在的问题是当协作者崩溃时,参与者一直阻塞。
与两阶段提交不同的是
还是要等到全局事务完毕资源才释放,占用资源大
Seata AT模式文档:https://seata.apache.org/zh-cn/docs/v1.6/dev/mode/at-mode
Seata AT模式的核心是对业务无侵入,是一种改进后的2PC
主要的实现是,在每个服务的数据库中新建一张undo_log表,结构如下:
content: 更改后的内容,
rollback_info: 回滚的内容
每个分支事务执行SQL都会解析SQL,保存content,rollback_info,插入到undo_log表中。如果全局事务通知需要回滚,去通过对比content和解析rollback_info,执行sql达到回滚的效果,如果全局事务通知全局事务成功,异步删除undo_log的记录。
这种方式不需要等待全局事务的提交才提交,能解决2PC、3PC资源占用的问题,实际就是异常就去补偿的思想
seata AT模式,事务是软状态需要考虑数据最终一致性,性能相对来说不是那么高,得去加锁,得动态去解析SQL插入数据库增加了和数据库的交互
Seata TCC模式文档:https://seata.apache.org/zh-cn/docs/v1.6/dev/mode/tcc-mode
TCC 是分布式事务中的二阶段提交协议,它的全称为 Try-Confirm-Cancel,即资源预留(Try)、确认操作(Confirm)、取消操作(Cancel),TCC模式对代码的入侵很大,但它的性能很好,还可以便捷的解决、空回滚、幂等、悬挂问题。
空回滚:没有执行try却执行了cancel。参与者分支注册完成之后会执行参与者一阶段try RPC 方法发送rpc时候网络延迟抖动,事务协调者全局回滚,参与者没有执行try却进入cancel
幂等:多次进入try。执行完二阶段之后,由于网络抖动或者宕机问题,会造成事务协调者收不到参与者执行confirm的返回结果,事务协调者会重复发起调用,直到二阶段执行结果成功
悬挂:执行了cancel又进入try。进入try 方法时,出现网路拥堵,由于 seata 全局事务有超时限制,执行 try 方法超时后,进行全局回滚,回滚完成后如果此时 RPC 请求才到达参与者 ,执行 try 方法进行资源预留,从而造成悬挂。
Seata社区这个博客写的挺好 https://seata.apache.org/zh-cn/blog/seata-tcc/ 解释了seata是怎么样处理解决、空回滚、幂等、悬挂问题。
假设一个转账需求,A给B转100,一般做法是判断够不够钱,如果够钱A-100,B+100
在TCC模式下把这个需求拆分为 Try-Confirm-Cancel
TCC模式有代码侵入,需要把一个业务拆分为三个方法,事务具有软状态,确认和取消操作都可能出现问题,需要考虑如何处理失败情况以保证最终一致性
参与评论
手机查看
返回顶部