现在我们开发的系统大部分是分布式系统,而分布式系统带来的分布式事务也成了标配。因为做系统肯定要用到事务,如果分布式系统,肯定要用分布式事务。目前的分布式事务有五种主流的解决方案,每种方案都有其各自的优缺点。
分布式事务实现主要有以下五种方案:
1. XA方案
2. TCC方案
3. 本地消息表
4. 可靠消息最终一致性方案
5. 最大努力通知方案。
XA是由X/Open组织提出的分布式事务框架,也可以叫协议。XA架构主要定义了全局事务管理器(TM)和局部资源管理器(RM)之间的接口。XA接口是双向的系统接口,在TM和一个或者多个RM之间形成通信桥梁。JTA是XA协议的JAVA实现。Atomikos是JTA的一个实现。
XA规范
- TM:Transaction Manager:事务管理器
- RM:Resource Manager:资源管理器即每个数据库资源
- AP:Application:应用程序即程序入口
- CRM:Communication Resource Manager:通信资源管理器
全局事务概念
- X/OPEN定义一套分布式模型和规范
- DTP:Distributed Transaction Processing Reference Model:分布式事务处理模型
- TM RM AP CRM等模型
- XA接口规范
- start-->end
- prepare-->commit/rollback
- JTA:实现XA接口分布式事务的AP适合单个系统同时操作多个库和表
2PC
- two phase commitment protocol:两阶段提交协议
- prepare和commit两阶段存在问题
- 同步阻塞:事务开启占用锁导致其他请求阻塞
- 单点故障:事务管理器发送prepare消息后宕机不发送commit消息导致其他请求阻塞
- 事务管理器多机热备:当TM进行切换时如果某个服务事务状态未同步过去,事务状态会丢失
- 脑裂问题:当分布式事务出现网络问题可能某个节点接收不到prepare或commit消息导致其他请求阻塞或者分布式事务出错
- 适合场景
- 单块应用跨多数据库业务场景(不适合微服务场景因为微服务下每个服务使用独立的数据库其他业务不依赖其数据库)
- 实现框架:JTA atomikos
- 原理图:
3PC
- three phase commitment protocol:三阶段提交协议
- 流程
- can commit消息,来试探各个库的连接和网络等问题是否能进行分布式事务
- can commit失败,发送abort消息关闭分布式事务
- can commit 成功,发送prepare commit消息,各个数据库开启事务,进行sql操作暂时不提交
- prepare commit 失败,发送abort各个事务进行rollback回滚
- prepare commit成功,发送do commit消息各个事务commit提交
- 超时机制当收到prepare commit消息执行本地事务成功后指定超时时间段没收到TM的commit则自动提交
- 缺陷
- 同步阻塞情况减少并没有真正解决
- 超时commit机制(可能存在自身prepare commit成功但是接受不到rollback回滚消息问题)
- 单点故障问题(同2PC)
TCC
- 常见TCC方案
- byte tcc:支持dubbo和spring cloud
- tcc transaction:不支持spring cloud
- hmily:都支持
- seata:阿里分布式框架
- tcc流程
- try:尝试去锁定资源;比如转账在try阶段进行金额的锁定 amount-10 locked_amount+10
- confirm:当try阶段都成功后,执行真正的业务逻辑;比如扣款 locked_amount-10
- cancel:如果try阶段或者confirm阶段失败则进行cancel;比如撤销扣款 amount+10 locked_amount-10
- 业务场景订单系统和库存系统提交订单:
- try阶段
- 保存一条订单记录订单的状态为未知状态
- 库存系统可用库存-1,冻结库存+1
- confirm阶段
- 订单状态修改为待支付
- 库存系统锁定库存+1 冻结库存-1
- cancel阶段
- 删除订单
- 库存系统可用库存+1 冻结库存-1
- 原理图
本地消息表(最终消息一致性)
- 执行流程
- 系统A写本地业务表
- 系统A写本地消息表,消息状态(待确认)
- 系统A本地事务成功后,发送消息到mq中
- 系统B消费mq中消息
- 系统B写本地消息表(消息表中消息唯一键防止重复消费)
- 系统B写本地业务表
- 系统B根据本地业务执行结果注册zk中节点目录
- 系统A感知到节点目录根据对于目录来更新消息表状态或回滚本地事务
- 系统A后台定时任务拉去本地消息表数据来重试
- 存在缺点:依赖本地数据库消息表数据库瓶颈
- 原理图
消息最终一致性
- 上游服务 消息服务 mq 下游服务
- 步骤1:上游服务发送一个待确认消息给消息服务
- 步骤2:消息服务保存消息成功后告诉上游服务
- 步骤3:上游服务执行本地事务,成功或失败通知消息服务
- 步骤4:消息服务收到成功则更新本地消息为已发送或已删除
- 步骤5:同时消息服务发送消息给mq(保证更新本地消息和发送消息到mq是在同一个事务中)
- 步骤6:下游服务消费消息,执行本地事务
- 步骤7:执行本地事务成功后执行ack,防止下游服务执行本地事务失败消息丢失
原理图
最大努力通知(最终消息一致性)
- 流程
- 系统A执行本地事务
- 系统A往mq中发送一条消息
- 最大努力通知服务消费消息
- 最大努力通知服务调用系统B来通知系统B直到成功为止(最大努力通知服务阶梯型的重试)
- 业务流程
- 阅饼充值成功后的短信推送
- 订单服务发送一条消息到mq中
- 通知服务收到消息后写入到本地业务表中,然后发送消息给mq
- 根据业务规则重试几次每次间隔多长时间发送失败后进行补发
saga事务
- 服务编码模式
- 每个服务提供do和compensate两个接口
- 消息通过mq来进行消息的传输
- 命令模式
- saga事务管理器来管理所有的服务接口
- saga事务管理器来调用compensate接口
byteTcc源码: http://note.youdao.com/s/ToAMxuGZ