常见的分布式事务方案
2PC 两阶段提交
角色定义:
TM:TransactionManager协调者
RM:RresourceManager参与者
从字面上理解两阶段提交分两个阶段提交的事务。
阶段一:
TM通知RM准备提交他们的事务分支,如果RM判断自己进行的工作可以被提交,就对工作内容进行持久化
再回复TM,否则异常。
Mysql是这样做的:事务管理器向所有涉及到的数据库发出prepare 准备提交请求,数据库收到请求后
执行修改和日志记录处理,处理完后把事务的状态标记成 可以提交,然后返回结果。
阶段二:
TM根据阶段一各个RM prepare的结果,决定提交还是回滚操作。所有的prepare都成功,就提交。如果有prepare失败的
则TM通知所有RM回滚自己的事务。
Mysql是这样做的:第一阶段提交的prepare成功,那么事务管理器向数据库服务器发出 确认提交 请求,数据库服务器把事务
的状态改成 提交完成 状态,然后返回。如果第一阶段内有任何一个数据库操作发生了错误,或者事务管理器收不到某个数据库的
响应,则认为事务失败,回滚所有的事务,数据库服务器收不到第二阶段的确认提交,也会把 可以提交的事务 回滚。
二阶段提交存在的问题:
- 同步阻塞
各个事务的ACID保证了全局的ACID所以需要用到串行化,
SERIALZABLE隔离级别。效率低。可重复度隔离级别无法保障分布式事务一致性。
- 单点故障
协调者TM有单点故障问题。
- 数据不一致
发送commit的时候有的参与者没接收到!
JTA/XA 规范实现
java对于事务的规范实现。管理多个connection事务的封装。有阻塞。
补偿机制
为了解决阻塞的问题,引入了补偿机制。
Seata AT模式实现
补偿机制实现的 应用层面的分布式事务框架。
TC,事务协调,独立出来。存储全局事务和分支事务信息。
开启事务的时候通过RPC向TC发送一个请求生成XID 全局事务ID。
RM:资源管理器
- 第一阶段
- 解析SQL
- 现场记录(查原始数据,并记录。)行锁,级别。
- 执行sql语句。
- 查下执行完成的日志存到 UndoLog
- Befor commit
- commit
- After local TX
- 第二阶段
如果成功:
- Find undo Log
- Delete Undo log
- commit
如果失败:
- Find Undo log
- 确认后置镜像是否可以回滚。
- 执行Undo SQL
- 删除 Undo SQL
- Commit
- After Local TX
- 设计优点
- 应用成实现,给予SQL解析和补偿。
- TC可以独立部署,避免单点故障。
- 通过全局(TC管理)锁实现了隔离与读隔离
- 存在的问题
- 数据局交互太多,效率低。性能损耗严重。
- 全局锁,力度也高
- 性价比不高
业务场景
注册,授权
订单服务,扣减库存,扣除余额。订单状态。
使用方式
微服务为例:
- 引入Seata包,nacos注册中心
- 添加Undo_log表
- 配置seata DataSourceProxy代理数据源
- 启动类排除DataSourceAutoCinfiguration,以免造成循环依赖。
- 添加seata配置 在nacos配置中心。存TC的配置。
- 使用 @GlobalTransactionnal
柔性事务 TCC (Try-Confirm-Cancel)
TCC和XA都是2阶段提交
实现有:
- TCC-Transaction
- Hmily
- ByteTCC
- EasyTransaction
- SeataTCC
XA是资源层面的分布式事务,强一致性,在两阶段提交的过程中,一直会持有资源的锁。
TCC是业务层的分布式事务,最终一致性,不会一直持有资源的锁。
优点: XA两阶段提交是资源层面的,而TCC是提升到了应用层面,避免了XA两阶段提交占用
资源锁时间过长导致的性能低下问题。
缺点: 主业务和从业务服务都需要改造,从业务改造成本更高,原来只需要提供一个接口,现在需要改造try,confirm,cancel三
个接口。开发成本高。
注意点
TCC需要把事务在应用层面切成两个阶段。
三种方法设计不好有很大bug,一般用以下方式:
TCC设计
- TCC异常控制
网络问题,重发,机器宕机等一系列问题。出现空回滚,幂等,悬挂问题。
TCC允许空回滚。
- TCC 防悬挂控制
要运行空回滚,必须拒绝空回滚之后的try操作。
案例:
try方法超时,这时候触发了cancel后try才到达。try是减少数据操作。这时候数据就出错了。
此时try方法不能被执行。
- TCC 幂等
网络故障情况下,分支事务可能多次被执行,需要支持幂等性。
使用方式
- 引入依赖
- 添加配置 Hmily配置
- 建Hmily的表
- 实现接口,添加 @Hmily注解,制定confirm方法,cancel方法
柔性事务,可靠消息最终一致性方案
可靠性消息最终一致性的方案是指当事务发起执行完成本地食物后并发出一条消息,事务参与方(消息消费者)一定能接受消息并处理事务成功,
此方案强调的是只要消息发给事务参与方最终事务要达成一致。
TCC比较难,业务侵入比较大。控制力比较强,适合金融类产品。
可靠新消息方案是从架构上避免分布式事务!
本地消息表
本地消息表,这个方案是ebay提出。核心是通过保证本地事务操作和消息的一致性。
- RockMq事务消息的方案
事务消息也有2PC,存在MqProducer RocketMq 4.3开始支持。
关键点,事务回查。
柔性事务,最大努力通知
- 消息重复通知机制
- 消息校对机制
可查消息最终状态
场景:比如公司内部实现了短信平台,所有的业务都接入这个短信平台,来实现发送短信的功能。
场景2:充值
最大通知与可靠新消息最终一致性的区别?
- 思想不同
可靠消息最终一致性,发起通知方需要保证消息发出去,并且将消息发送到接收方,消息的
可靠性关键由发起通知放来保证。 - 业务场景不同
可靠消息最终一致性关注的是交易过程的事务一致性,以异步的方式完成交易。
最大努力通知关注的是交易后的通知事务,即将交易结果可靠的通知出去。 - 技术解决方向不同
可靠消息最终一致性要解决消息从发出到接收的一致性,既消息发出并且被接收到。
最大努力通知无法保证消息从发出到接收的一致性,只提供消息接收的可靠性机制。可靠性机制是,最大努力的将消息通知给
接收方,当消息无法被接收方接收时,由接收方主动查询消息。