分布式事务,事务失效,TC事务协调者
1. 概述
本方案书旨在解决分布式系统中事务一致性问题,重点阐述全局事务标识(XID)的传递与存储机制、事务协调者(TC)的设计与部署,以及分布式事务失效场景的应对策略。基于业界成熟框架(如Seata)的最佳实践,提供高可用、高性能的分布式事务解决方案。
2. 核心概念与架构
2.1 分布式事务模型
- AT模式(自动补偿)
通过全局锁和逆向SQL(undo log)实现自动回滚,适用于数据库操作。 - TCC模式(手动补偿)
业务层实现Try-Confirm-Cancel
接口,适用于复杂业务逻辑。 - Saga模式(长事务)
通过事件驱动和补偿事务链实现最终一致性,适用于跨服务长流程。
2.2 核心组件
组件 | 角色 |
---|---|
事务协调者(TC) | 独立服务,负责全局事务状态管理和协调。 |
事务管理器(TM) | 集成在业务服务中,负责开启/提交/回滚全局事务。 |
资源管理器(RM) | 管理本地资源(如数据库、消息队列),执行分支事务的提交/回滚。 |
3. XID 的传递与存储机制
3.1 XID 的生成与作用
- 全局唯一性:由TC生成,格式示例:
192.168.1.100:8091:123456
。 - 核心功能:关联所有分支事务,确保跨服务、跨资源的操作原子性。
3.2 XID 的传递方式
场景1:跨服务调用(RPC/HTTP)
- 隐式传递(推荐)
通过RPC框架(如Dubbo、Feign)的请求头自动传递XID。// Feign 拦截器示例 public class XidFeignInterceptor implements RequestInterceptor { @Override public void apply(RequestTemplate template) { String xid = RootContext.getXID(); template.header("TX_XID", xid); } }
场景2:异步任务与多线程
- 手动传递(基础方案)
显式传递XID并在子线程中绑定:String xid = RootContext.getXID(); executor.submit(() -> { RootContext.bind(xid); try { // 业务逻辑 } finally { RootContext.unbind(); } });
- TransmittableThreadLocal(推荐)
使用阿里开源工具解决线程池上下文传递问题:private static TransmittableThreadLocal<String> XID = new TransmittableThreadLocal<>(); executor.submit(TtlRunnable.get(() -> { // 子线程自动继承XID }));
场景3:消息队列(如Kafka、RocketMQ)
- 消息头传递
生产者将XID写入消息属性,消费者读取后绑定到本地事务:// RocketMQ 生产者 Message msg = new Message(); msg.putUserProperty("XID", xid);
3.3 XID 的存储管理
- 默认存储:基于
ThreadLocal
,仅限当前线程访问。public class RootContext { private static ThreadLocal<String> XID = new ThreadLocal<>(); }
- 扩展方案:
InheritableThreadLocal
:支持父子线程传递,但不适用于线程池。TransmittableThreadLocal
:支持线程池场景,需配合TtlRunnable
使用。
4. 事务协调者(TC)的设计与部署
4.1 TC 的核心职责
- 全局事务管理:注册XID,维护
global_table
(全局事务状态表)。 - 分支事务协调:记录
branch_table
(分支事务表),驱动提交/回滚。 - 故障恢复:检测超时事务,触发自动回滚或重试。
4.2 部署模式
独立服务部署(推荐)
- 启动命令:
# Seata TC 服务启动 sh seata-server.sh -p 8091 -m db -h 192.168.1.100
- 高可用设计:
- 数据库模式:多个TC节点共享同一数据库(如MySQL)。
- Raft模式:通过一致性协议实现集群选举(Seata 1.5+)。
配置文件示例(Seata)
# registry.conf
registry { type = "nacos" nacos { serverAddr = "localhost:8848" namespace = "" cluster = "default" }
} config { type = "nacos" nacos { serverAddr = "localhost:8848" namespace = "" group = "SEATA_GROUP" }
}
4.3 事务恢复流程
- 回滚触发:TM通知TC回滚指定XID。
- 分支查询:TC从
branch_table
查询所有关联分支。 - 逆向执行:各RM根据
undo_log
执行补偿操作。
5. 分布式事务失效场景与解决方案
5.1 常见失效场景
场景 | 原因 | 后果 |
---|---|---|
RPC调用未透传XID | 未在请求头中添加XID | 下游服务无法加入全局事务 |
异步任务未绑定XID | 子线程未继承ThreadLocal 中的XID | 异步操作游离于事务外 |
TC单点故障 | 未部署TC集群 | 全局事务无法协调 |
5.2 解决方案
场景1:XID传递失败
- 强制校验:在事务入口拦截请求,校验XID是否存在。
- 统一拦截器:通过RPC拦截器(如Spring AOP)自动透传XID。
场景2:多线程XID丢失
- TransmittableThreadLocal:结合线程池包装类(
TtlExecutors
)传递上下文。 - 框架集成:Spring异步任务支持:
@Configuration @EnableAsync public class AsyncConfig implements AsyncConfigurer { @Override public Executor getAsyncExecutor() { return TtlExecutors.getTtlExecutor(Executors.newFixedThreadPool(4)); } }
场景3:TC单点故障
- 集群部署:部署多个TC节点,通过Nacos或Raft协议实现高可用。
- 健康检查:集成Prometheus监控TC节点状态。
6. 最佳实践与性能优化
6.1 最佳实践
- 避免跨事务异步操作:将异步任务放在事务边界外。
- 事务粒度控制:单个事务内操作不超过3个服务。
- 超时配置:全局事务超时时间建议为60秒,避免资源长时间锁定。
6.2 性能优化
- 异步提交:Seata AT模式下,二阶段提交异步化(默认开启)。
- 全局锁优化:减少热点数据竞争,使用
SELECT ... FOR UPDATE
时指定索引。
6.3 监控与日志
- 监控指标:
- TC节点状态(活跃事务数、TPS)。
- RM资源锁竞争情况。
- 日志收集:
- 记录全局事务ID(XID)到ELK或SkyWalking,支持快速定位问题。
7. 附录
7.1 示例代码仓库
- Seata 官方示例
- TransmittableThreadLocal 使用指南
7.2 相关工具
- Seata Dashboard:可视化监控全局事务状态。
- Prometheus + Grafana:监控TC集群性能指标。
版本记录
- v1.0(2023-10-01):初版发布,涵盖XID传递、TC设计、失效场景解决方案。
- v1.1(2023-10-05):补充异步任务配置示例与性能优化建议。
评审人:技术委员会
批准人:CTO
注:本方案书需结合具体业务场景调整,建议在预生产环境充分验证。