分布式事务的核心问题与CAP约束
微服务架构下,一个业务操作可能跨多个服务的数据修改,例如电商下单同时涉及库存扣减、订单创建、账户扣款。每个服务有独立数据库,本地事务无法保证跨库一致性。分布式事务要解决的就是跨服务的数据一致性问题。
CAP定理决定了分布式事务的取舍:CP方案(如两阶段提交2PC)保证强一致但牺牲可用性,网络分区时事务阻塞;AP方案(如最终一致性/Saga)保证可用性但允许暂时不一致。生产环境中,纯粹CP方案几乎不被采用——事务协调器单点故障会锁死全局,影响面远超数据不一致。Seata的AT模式和TCC模式分别代表了”近CP”和”近AP”的工程实践路线。
Seata AT模式:零侵入自动补偿的实现原理
AT模式的核心思路是拦截SQL执行,自动记录修改前后的数据快照(before/after image),回滚时用before image反向补偿。业务代码无需感知分布式事务的存在,只需加一个@GlobalTransactional注解:
@GlobalTransactional(timeoutMills = 60000)
public void purchase(Long userId, Long productId, int count) {
// 扣减库存
stockService.deduct(productId, count);
// 创建订单
orderService.create(userId, productId, count);
// 扣减账户余额
accountService.debit(userId, productPrice * count);
}
AT模式的执行流程:一阶段——拦截业务SQL,查询修改前数据生成before image,执行业务SQL,查询修改后数据生成after image,生成回滚日志写入undo_log表,提交本地事务(释放本地锁)。二阶段提交——异步删除undo_log。二阶段回滚——读取undo_log的before image,校验当前数据是否被脏写(after image与当前数据对比),校验通过则执行反向SQL补偿,校验不通过则转入人工介入。
AT模式的隐性成本:undo_log表每个参与方数据库都需要创建;一阶段本地事务提交前需要额外两次SELECT(before/after image)和一次INSERT(undo_log),性能损耗约15%-25%。undo_log的数据量与业务数据量等比增长,需要定期清理已提交的记录。
TCC模式:手动补偿的精细控制方案
TCC(Try-Confirm-Cancel)模式要求业务方实现三个接口:Try预留资源(冻结库存、冻结余额)、Confirm确认执行(扣减冻结的库存和余额)、Cancel取消执行(释放冻结的资源)。相比AT的自动补偿,TCC需要业务方编码三个阶段,侵入性强但控制粒度更细:
public interface StockTccService {
// Try: 冻结库存
@TwoPhaseBusinessAction(name = "stockTcc",
commitMethod = "confirm", rollbackMethod = "cancel")
boolean deduct(@BusinessActionContextParameter(paramName = "productId") Long productId,
@BusinessActionContextParameter(paramName = "count") int count);
// Confirm: 确认扣减冻结库存
boolean confirm(BusinessActionContext context);
// Cancel: 释放冻结库存
boolean cancel(BusinessActionContext context);
}
// Try阶段SQL: UPDATE stock SET frozen = frozen + #{count} WHERE id = #{productId}
// Confirm阶段SQL: UPDATE stock SET count = count - #{count}, frozen = frozen - #{count} WHERE id = #{productId}
// Cancel阶段SQL: UPDATE stock SET frozen = frozen - #{count} WHERE id = #{productId}
TCC模式没有undo_log开销,Try阶段的资源冻结本身就是隔离手段。但TCC有一个AT没有的问题——空回滚和悬挂:Try请求因网络超时未到达服务端,事务协调器已触发Cancel,Cancel执行时发现没有Try记录(空回滚);后续Try请求到达并执行了资源冻结,但事务已经Cancel完毕(悬挂),冻结资源永远不会被释放。解决方案是引入事务控制表记录Try是否执行过:
// Cancel阶段判断空回滚
if (tryRecord == null) {
// 空回滚:插入带rollback标记的记录
insertTxRecord(xid, "rollback");
return true;
}
// 悬挂判断:如果记录已标记rollback,拒绝执行Try
if (txRecord.status == "rollback") {
return false;
}
AT与TCC的生产选型决策矩阵
AT模式适用场景:SQL驱动的CRUD业务(如订单、账户)、开发效率优先、团队能力不足以实现精细补偿逻辑。AT的强项是低代码侵入,业务方几乎无需修改代码。
TCC模式适用场景:非SQL操作(如调用第三方支付接口、发送消息)、性能敏感(undo_log开销不可接受)、需要精细化资源隔离(如库存冻结而非直接扣减)。TCC的强项是事务边界完全由业务控制,可处理非数据库资源的补偿。
混合方案在大型系统中常见:核心资金链路用TCC保证补偿可控,外围业务用AT降低开发成本。Seata支持同一全局事务内AT和TCC分支混合,协调器统一管理。
Seata Server生产部署与性能调优
Seata Server(TC)是事务协调器,生产环境必须集群部署。存储模式选db(MySQL/PostgreSQL),不要用file模式(无法多实例共享状态)。集群规模建议3节点,前端Nginx做负载均衡。TC的JVM配置根据事务并发量调整:
# 高并发场景JVM参数
JAVA_OPT="-server -Xms2g -Xmx2g -XX:+UseG1GC
-XX:MaxGCPauseMillis=50
-Dseata.config.nacos.server-addr=nacos:8848
-Dseata.registry.nacos.namespace=seata"
关键参数调优:seata.server.maxCommitRetryTimeout(提交重试超时,默认100ms,网络抖动场景建议调到500ms);seata.server.rollbackRetryTimeoutUnlockEnable(回滚超时后是否释放锁,默认false,高并发场景建议true防止死锁);seata.client.undo.logSerialization(undo_log序列化方式,默认jackson,生产建议protostuff减少序列化开销约40%)。
原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/fen-bu-shi-shi-wu-seataat-mo-shi-yu-tcc-bu-chang-ji-zhi/