微服务
微服务
1 分布式事务
分布式事务概述
以下是有研究的事务介绍。
- Spring Boot
- JTA:内置功能,不需要特殊处理。
- sample-atomiko:基于 Atomiko 实现多数据源数据库的分布式事务
- Seata:基于 Seata 实现多节点分布式事务。
分布式事务解决方案
- 技术方案
- 基于 XA 协议(数据库层面)
- 2PC、3PC
- 基于补偿和最终一致性
- AT
- TCC
- Saga
- 本地消息
- 消息队列
- 基于 XA 协议(数据库层面)
- 实现方式
- Seata
- XA
- AT
- TCC
- Saga
- RocketMQ 等消息中间件
- 消息队列
- 本地消息表
- 本地消息
- Seata
2 PC
2PC 概念
2PC(Two-Phase Commit,两阶段提交)是一种经典的分布式事务协议,用于确保在分布式系统中所有参与者要么全部提交事务,要么全部回滚事务,从而保证数据的一致性。2PC 的参与角色如下。
- 协调者(Coordinator):负责协调整个事务的提交或回滚。
- 参与者(Participant):实际执行事务的各个节点/服务。
2PC 执行流程
2PC 执行流程分为 2 个阶段,如下。
- 第一阶段:准备阶段(投票阶段)
- 1 协调者发送准备请求:协调者向所有参与者发送 prepare 请求,询问是否可以提交事务
- 2 参与者执行事务:每个参与者执行事务操作,但不真正提交
- 3 参与者记录日志:参与者将事务信息写入日志(用于故障恢复)
- 4 参与者响应准备结果:
- 4.1 如果事务可以提交,返回 Yes(就绪)响应
- 4.2 如果事务不能提交(如出现冲突或错误),返回 No(中止)响应
- 第二阶段:提交阶段(执行阶段),根据第一阶段的投票结果,有两种情况
- 情况 1:所有参与者都返回Yes
- 1 协调者发送提交命令:协调者向所有参与者发送 commit 请求
- 2 参与者完成提交:参与者正式提交事务,释放锁资源
- 3 参与者发送确认:参与者向协调者发送 ack 确认
- 4 协调者完成事务:收到所有参与者的 ack 后,协调者结束事务
- 情况 2:任一参与者返回 No 或超时未响应
- 1 协调者发送回滚命令:协调者向所有参与者发送 rollback 请求
- 2 参与者执行回滚:参与者根据日志回滚事务,释放锁资源
- 3 参与者发送确认:参与者向协调者发送 ack 确认
- 4 协调者结束事务:收到所有参与者的ack后,协调者结束事务
- 情况 1:所有参与者都返回Yes
2PC 的优缺点
- 优点
- 强一致性:保证所有节点要么全部成功,要么全部失败
- 简单易懂:协议流程清晰,易于理解和实现
- 缺点
- 同步阻塞:在准备阶段后,参与者会一直阻塞等待协调者的决定
- 单点故障:协调者故障可能导致系统阻塞
- 数据不一致风险:在第二阶段,如果部分参与者未收到 commit/rollback 命令,可能导致数据不一致
- 性能开销:需要多次网络通信和日志写入,性能较低
3 PC
3PC 概念
3PC(Three-Phase Commit,三阶段提交)是分布式系统中用于保证事务原子性的一种协议,是 2PC(两阶段提交)的改进版本。它通过增加一个准备阶段来减少阻塞问题,提高了系统的可用性。
3PC 执行流程
3 PC 的 3 个阶段如下。
- 第一阶段:CanCommit 阶段
- 协调者行为
- 1 向所有参与者发送 CanCommit 请求
- 2 询问参与者是否可以执行事务提交
- 3 等待参与者响应
- 参与者行为
- 1 检查自身状态(资源是否锁定、日志是否可写等)
- 2 如果能够提交,返回 Yes 响应;否则返回 No 响应
- 协调者行为
- 第二阶段:PreCommit 阶段,根据第一阶段的响应,协调者决定是否继续
- 情况一:所有参与者返回 Yes
- 协调者发送 PreCommit 请求
- 参与者执行事务操作但不提交,记录 undo/redo 日志
- 参与者返回 Ack 响应
- 情况二:有参与者返回 No 或超时
- 协调者发送 abort 请求
- 参与者中断事务
- 情况一:所有参与者返回 Yes
- 第三阶段:DoCommit 阶段
- 正常提交流程:
- 1 协调者收到所有 PreCommit 的 Ack 后,发送 DoCommit 请求
- 2 参与者完成事务提交,释放资源
- 3 参与者返回 HaveCommitted 响应
- 中断流程:
- 1 协调者没有收到所有 PreCommit 的 Ack
- 2 协调者发送 abort 请求
- 3 与者使用 undo 日志回滚事务
- 正常提交流程:
3PC 和 2PC 的主要区别
- 增加 CanCommit 阶段:提前发现不可行的事务,减少资源锁定时间
- 超时机制:参与者在 PreCommit 阶段后如果长时间未收到协调者消息,会自动提交
- 减少阻塞:降低了协调者单点故障导致的长时间阻塞问题
3PC 的优缺点
- 优点
- 相比 2PC 减少了阻塞时间
- 通过预检查降低了资源浪费
- 超时机制提高了系统可用性
- 缺点
- 实现复杂度高于 2PC
- 网络通信次数增加
- 仍然存在数据不一致的可能性(如网络分区时)
AT
AT 概述
AT(Auto Transaction)模式是 Seata 框架提出的无侵入分布式事务解决方案,基于两阶段提交(2PC)演进而来。其核心特点是:
- 业务无侵入:不需要业务代码改造
- 自动补偿:自动生成反向 SQL 实现回滚
- 基于数据库本地事务:通过全局锁保证隔离性
AT 整体架构
[TM] → [TC] ← [RM]
│ │ │
│ │ ├─ 阶段一:注册分支 + 提交本地事务
│ │ └─ 阶段二:上报状态 + 异步清理
- TM (Transaction Manager):定义全局事务边界(@GlobalTransactional)
- TC (Transaction Coordinator):事务协调器(Seata Server)
- RM (Resource Manager):资源管理器,管理分支事务
AT 执行流程(正常提交)
- 阶段一:执行与提交
- 1 TM 开启全局事务
- TM 向 TC 申请 XID(全局事务 ID)
- 示例:@GlobalTransactional 注解的方法被调用
- 2 RM 执行本地事务
- 业务 SQL 执行前,RM 拦截 SQL:
- 解析 SQL 语义(INSERT/UPDATE/DELETE)
- 查询前置镜像(before image)
- 执行业务 SQL
- 查询后置镜像(after image)
- 生成 undo_log 记录(包含前后镜像)
- 提交本地事务(业务 SQL 和 undo_log 在同一个本地事务)
- 业务 SQL 执行前,RM 拦截 SQL:
- 3 注册分支事务
- RM 向 TC 注册分支事务,关联 XID
- 获取相关数据的全局锁
- 1 TM 开启全局事务
- 阶段二:全局提交
- 1 TM 通知 TC 提交
- 所有分支事务执行成功后,TM向TC发起全局提交
- 2 TC 异步清理
- TC 异步删除各 RM 的 undo_log 记录
- 释放全局锁
- 1 TM 通知 TC 提交
AT 执行流程(异常回滚)
- 阶段一:执行与提交
- 同正常流程
- 阶段二:全局回滚
- 1 TM 通知 TC 回滚
- 任一分支事务失败时,TM 向 TC 发起全局回滚
- 2 RM 执行补偿
- TC 通知各 RM 回滚
- RM 查询 undo_log 记录
- 校验脏写(对比当前数据与后置镜像)
- 根据 before image 生成反向 SQL 执行回滚
- 删除 undo_log 记录
- 释放全局锁
- 1 TM 通知 TC 回滚
AT 核心机制
全局锁设计
- 避免其他事务修改正在处理的数据
- 实现方式:SELECT FOR UPDATE 或 TC 维护的内存锁
undo_log 表结构。
CREATE TABLE `undo_log` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`branch_id` bigint(20) NOT NULL,
`xid` varchar(100) NOT NULL,
`context` varchar(128) NOT NULL,
`rollback_info` longblob NOT NULL, -- 包含前后镜像数据
`log_status` int(11) NOT NULL,
`log_created` datetime NOT NULL,
`log_modified` datetime NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`)
);
数据镜像生成规则。
- INSERT:before image 为空,after image 为插入数据
- UPDATE:before image 为更新前数据,after image 为更新后数据
- DELETE:before image 为删除前数据,after image 为空
AT 特点
- 优势
- 零侵入:无需业务代码改造
- 高性能:本地事务一阶段立即提交
- 自动补偿:无需手动编写反向逻辑
- 支持多语言:通过 SQL 解析实现跨语言支持
- 限制
- 仅支持关系型数据库
- 隔离级别为读未提交(需业务容忍脏读)
- UPDATE/DELETE 操作需有主键
- 高并发场景全局锁可能成为瓶颈
TCC
TCC 概念
TCC(Try-Confirm-Cancel)是一种补偿型分布式事务解决方案,通过业务逻辑层面的设计来实现最终一致性。它将一个完整的业务逻辑拆分为三个操作。
- Try:预留业务资源
- Confirm:确认执行业务操作
- Cancel:取消业务操作并释放资源
TCC 执行流程
TCC 的三个阶段。
- 尝试阶段:Try 阶段。
- 目的:完成所有业务检查,预留必要的业务资源
- 执行流程
- 1 事务协调器向所有参与者发送 Try 请求
- 2 各参与者执行:
- 2.1 检查业务一致性条件(如库存是否充足)
- 2.2 预留资源(如冻结库存、预扣金额)
- 2.3 记录事务日志(用于后续 Confirm 或 Cancel)
- 3 参与者返回执行结果(成功/失败)
- 特点
- 此阶段不会真正完成业务操作
- 所有操作必须幂等(重复调用效果相同)
- 确认阶段:Confirm 阶段
- 前提条件: 所有参与者的 Try 阶段都执行成功
- 执行流程
- 1 事务协调器向所有参与者发送 Confirm 请求
- 2 各参与者执行:
- 2.1 使用 Try 阶段预留的资源完成实际业务操作
- 2.2 提交事务(如扣减实际库存、完成支付)
- 2.3 更新事务状态为"已完成"
- 3 参与者返回执行结果
- 特点
- 此阶段操作必须幂等
- 默认 Confirm 一定会成功(因为 Try 已预留资源)
- 取消阶段:Cancel 阶段
- 触发条件:任一参与者的 Try 阶段执行失败
- 执行流程
- 1 事务协调器向所有参与者发送 Cancel 请求
- 2 各参与者执行:
- 2.1 释放 Try 阶段预留的资源(如解冻库存、返还预扣金额)
- 2.2 更新事务状态为"已取消"
- 3 参与者返回执行结果
- 特点
- 参与者返回执行结果
- 需要处理 Try 阶段可能部分成功的情况
TCC 事务管理器的工作流程。
- 开始全局事务:生成全局事务 ID
- 调用 Try 阶段:按业务逻辑顺序调用各服务的 Try 接口
- 决策阶段:
- 所有 Try 成功 → 进入 Confirm 阶段
- 任一 Try 失败 → 进入 Cancel 阶段
- 4 执行 Confirm/Cancel:根据决策结果调用相应接口
- 5 记录事务状态:持久化事务最终状态
TCC 模式的特点
- 优点
- 高可用性:没有全局锁,减少资源锁定时间
- 高性能:Try 阶段完成后,Confirm 通常很快
- 灵活性:业务可定制化程度高
- 最终一致性:保证系统最终一致
- 缺点
- 开发复杂度高:每个业务都需要实现三个接口
- 业务侵入性强:需要改造现有业务逻辑
- 一致性风险:Confirm/Cancel 可能失败(需重试机制)
TCC 与 2PC/3PC 的关键区别
- 实现层面
- 2PC/3PC:数据库/资源管理器层面
- TCC:业务应用层面
- 锁定范围
- 2PC/3PC:锁定整个资源
- 2PC/3PC:锁定整个资源
- 事务持续时间
- 2PC/3PC:整个事务期间都持有锁
- TCC:Try 阶段后即可释放大部分资源
Saga
Saga 概述
Saga 是一种长事务解决方案,核心思想是将一个长事务拆分为多个本地事务,每个本地事务都有对应的补偿操作。Saga 特别适合以下场景:
- 长时间运行的业务过程(分钟/小时级)
- 跨多个服务的业务流
- 无法实现资源锁定的系统
Saga 的两种实现方式
- 1 协同式(Choreography)
- 通过事件驱动架构实现
- 各服务监听上游事件并触发本地事务
- 无中心协调器,服务间直接通信
- 2 编排式(Orchestration)
- 通过中心协调器(Orchestrator)控制流程
- 协调器按顺序调用各服务并处理异常
- 更易维护和监控(推荐方式)
Saga 核心执行流程(编排式)
- 正常流程
[协调器] → [服务A:事务T1] → [服务B:事务T2] → [服务C:事务T3] → 完成
- 1 协调器开始 Saga 事务
- 2 依次调用各服务的正向操作(T1, T2, T3...)
- 3 所有正向操作成功则事务完成
- 异常回滚流程
[协调器] → [服务A:T1] → [服务B:T2失败] → [服务B:C2] → [服务A:C1] → 终止
- 1 当某个正向操作(如T2)失败时:
- 2 协调器按反向顺序调用已成功服务的补偿操作(C2, C1...)
- 3 所有补偿完成后事务终止
Saga 关键设计要素
- 1 补偿操作要求
- 1.1 幂等性:可重复执行不影响结果
- 1.2 可交换性:补偿顺序不影响最终状态
- 1.3 可丢弃性:补偿成功后不再需要保留历史
- 2 事务日志记录,示例日志如下
- 3 异常处理机制
- 3.1 超时重试:对暂时性故障自动重试
- 3.2 人工干预:无法自动恢复时告警
- 3.3 最终一致性:允许短暂不一致但最终一致
Saga 特点
- 优势
- 无长期资源锁定:适合长事务场景
- 高可用性:部分服务不可用时仍可推进
- 业务灵活性:可组合异构系统
- 可扩展性:易于添加新服务节点
- 挑战
- 补偿逻辑复杂:需要设计完善的补偿机制
- 调试困难:分布式环境问题定位复杂
- 弱隔离性:可能出现脏读(需业务容忍)
本地消息表
简单的整理下,流程仅供参考,说明简单了。
- 创建一个本地消息表
- 本地服务执行本地事务并提交,将要发送的消息插入本地消息表中
- 通过定时任务扫描未发送的消息,通过消息队列发送消息
- 另一个服务接收消息执行事务,执行失败就重试,多次重试失败就放入死信队列,人工干预处理。
消息队列
简单的整理下,流程仅供参考,说明简单了。
- 使用 RocketMQ 实现事务消息,将消息发送和本地事务的执行实现强一致性。
- 如果发送消息成功,另一个服务接收消息执行事务,执行失败就重试,多次重试失败就添加到本地消息表中,人工干预处理。
atomikos
1. atomikos 应用场景
基于XA协议的多资源协调 (完全支持)
✅ 跨数据库节点 (MySQL、Oracle等支持XA的数据库)
✅ 数据库+消息队列 (如ActiveMQ等支持XA的消息中间件)
✅ 同架构跨服务节点 (所有服务使用相同Atomikos配置)不支持的分布式场景
❌ 跨微服务的事务 (服务间RPC调用)
❌ 非XA资源的事务 (如MongoDB、Redis等不支持XA协议的NoSQL)
❌ 跨异构系统的事务 (不同编程语言/技术栈的系统)
2. 快速上手
有如下两个使用方式,详见示例代码 sample-atomikos。
基于原有多数据源支持改造上手。
(1) 引入 maven 依赖。
(2) 配置 DataSource。见 FirstDbConfig。简单的快速上手示例。
见 AtomikosQuickTest main 方法。