通俗易懂分布式事务之2PC、3PC、Seata AT模式、Seata TCC模式 ...

宁睿  论坛元老 | 2025-3-27 17:35:36 | 显示全部楼层 | 阅读模式
打印 上一主题 下一主题

主题 1958|帖子 1958|积分 5884

通俗易懂分布式事务之2PC、3PC、AT、TCC

单机服务事务提交回滚操作是需要拿到Connection对象,调用提交commit方法或者rollback方法回滚的,比方下面操作
  1. Connection conn = DriverManager.getConnection(...);
  2. try{
  3.   con.setAutoCommit(false);
  4.   Statement stmt = con.createStatement();
  5.   //1 or more queries or updates
  6.   con.commit();
  7. }catch(Exception e){
  8.   con.rollback();
  9. }finally{
  10.   con.close();
  11. }
复制代码
要想提交或者回滚,必须拥有Connection对象,然而在分布式情况,jvm都是不同的,自然就拿不到其他服务的Connection对象,所以在分布式情况,我们无法保证原子性。因此分布式事务就需要另寻出路。
1. 术语


  • 全局事务:分布式情况,操作涉及很多服务,全局事务能保证这些服务的原子性,要提交就全部服务都会提交,要回滚全部服务都会回滚
  • 分支事务:分支事务建立在全局事务当中,是属于单个服务的事务
2. 2PC

2PC 即两阶段提交协议,是将整个事务流程分为两个阶段,准备阶段(Prepare phase)、提交阶段(commit phase)
与本地事务的区别就是 会加入一个事务协调者的角色,这个事务协调者控制整体的事务提交
2.1 流程


  • PreCommit阶段
    事务协调者开启全局事务,参与者向事务协调者注册分支事务,然后走自己的业务代码,在这过程中如果发生了非常参与者会发送信息到事务协调者我非常了,需要全部回滚。如果统统正常,告诉事务协调者我可以提交了,等待事务协调者发送commit指令
  • Commit阶段
    事务协调者向所有分支事务发送commit指令,释放数据库资源
在这里举个具体的例子
我们有两个服务:订单服务、库存服务
用户需要买东西,起首要创建订单,创建订单前需要去锁定库存,然后再去创建订单。
  1. 1. 事务协调者开启全局事务
  2. 2. 库存服务开启事务,向事务协调者注册一个分支事务
  3. 3. 库存服务锁定库存
  4. 4. 订单服务开启事务,向事务协调者注册一个分支事务
  5. 5. 订单服务创建订单
  6. 6. 事务协调者进行全局提交事务
  7. 7. 库存服务提交事务
  8. 8. 订单服务提交事务
  9. 9. 全局事务完成
复制代码
在任何一个流程中非常,事务协调者都会发起全局回滚事务,这种方式,在全局事务完成前,Connection对象都不会释放,因为你释放了你就无法控制了,缺点很明显,如果订单服务需要处置惩罚很久,库存服务Connection对象都不会释放,一直占用着,这种是强原子性的很浪费资源
2.2 实现2PC

XA协议,是X/Open组织提出的跨异构技术实现2PC的接口尺度。
使用XA协议起首条件就是,需要关系型数据库支持,目前主流数据库:

  • MySQL: InnoDB 引擎支持 XA 事务,并实现了 XA 接口。
  • PostgreSQL: 从版本 8.0 开始支持 XA 事务,通过插件接口实现。
  • Oracle: 支持 XA 事务,使用 OracleXADataSource 提供 XA 接口。
  • SQL Server: 支持 XA 事务,使用 MSDTC 提供 XA 接口。
  • DB2: 支持 XA 事务,使用 DB2 Universal JDBC 驱动器提供 XA 接口。
  • Sybase ASE: 支持 XA 事务,使用 JConnect 提供 XA 接口。
2.2.1 MySQL实现流程:


  • START
第一步开启XA事务
库存DB:
  1. XA START 'xid'
  2. UPDATE  product SET num = num - 1 where id = 100
  3. XA END 'xid'
复制代码
订单DB:
  1. XA START 'xid'
  2. INSERT INTO order values(xxx)
  3. XA END 'xid'
复制代码

  • PREPARE
第二步,准备就绪,等待事务协调者同意我提交
库存DB:
  1. XA PREPARE 'xid'
复制代码
订单DB:
  1. XA PREPARE 'xid'
复制代码

  • commit
全部提交
库存DB
  1. XA COMMIT 'xid'
复制代码
订单DB:
  1. XA COMMIT 'xid'
复制代码
2.2.3 seata支持XA

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
2.3 2PC缺陷


  • 强同等性,每个分支事务得等待所有分支事务都准备好,才气提交释放
  • 协调者发生故障。分支事务会一直阻塞下去。
2. 3PC

三阶段提交协议(3PC)重要是为相识决两阶段提交协议的阻塞问题,2pc存在的问题是当协作者崩溃时,参与者一直阻塞。
与两阶段提交不同的是

  • 引入超机会制
  • 在最前面引入CanCommit阶段,为了防止参与者服务不可用,询问各个服务能不能开启事务
2.1 流程:


  • CanCommit阶段
    事务询问阶段,协调者向参与者发送CanCommit哀求,询问是否可以执行事务提交操作。如果有参与者返回NO,就不进行下一步操作了
  • PreCommit阶段
    进入PreCommit后就和2PC一样了,区别就是有超机会制
  • Commit阶段
    提交,释放数据库资源,与2PC一样
2.2 缺点

还是要等到全局事务完毕资源才释放,占用资源大
3. Seata AT模式

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资源占用的问题,实际就是非常就去补偿的思想
3.1 流程


  • 事务入口会开启全局事务,分支事务注册事务(非入口事务通过传递xid来判断是加入哪个事务)
  • 分支事务执行SQL之前会剖析SQL,生成前置镜像
  • 剖析出SQL如果是更新修改数据这种情况,seata会锁住更新修改的数据,另一个全局事务进来想要更改更新修改的数据就需要等待全局事务结束才气进行更改对应着seata的lock_table表

  • 执行SQL
  • 根据SQL生成后置镜像,如果到时候需要回滚,直接执行后置镜像
  • 插入数据到undo_log表
  • 提交本地事务
  • 如果所有分支事务都是正常的,就释放锁,然后异步删除所有的分支事务undo_log的记录
  • 如果发生非常,所有事务都对前置镜像进行剖析,生成SQL,执行回滚操作
  • 如果发生非常,需要回滚,后置镜像当中的值,不等于当前值,代表有其他业务一样的更改了这行数据,这时候就需要人工行止理了(往往如果设置了数据库字段update_time主动更新时间会很容易导致这个情况出现,不能用数据库的主动更新时间,主动更新时间得去到业务代码实现里)
3.2 缺点

seata AT模式,事务是软状态需要考虑数据终极同等性,性能相对来说不是那么高,得去加锁,得动态去剖析SQL插入数据库增加了和数据库的交互
4. Seata TCC模式

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是怎么样处置惩罚解决、空回滚、幂等、悬挂问题。
4.1 流程

假设一个转账需求,A给B转100,一般做法是判断够不够钱,如果够钱A-100,B+100
在TCC模式下把这个需求拆分为 Try-Confirm-Cancel

  • try 阶段,查询A够不够钱,够的话冻结A 100块钱
  • confirm阶段,执行业务代码,预先给B + 100
  • 如果统统正常,执行A-100,B + 100
  • 如果有非常,执行try的回滚逻辑,A排除冻结的100
4.2 缺点

TCC模式有代码侵入,需要把一个业务拆分为三个方法,事务具有软状态,确认和取消操作都可能出现问题,需要考虑如那边理失败情况以保证终极同等性

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

宁睿

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表