分布式事务是微服务架构中保证跨服务数据一致性的核心机制。Seata是阿里巴巴开源的分布式事务解决方案,提供AT、TCC、SAGA、XA四种事务模式。AT模式基于SQL解析自动生成补偿操作,接入成本最低;TCC模式需要业务代码实现Try-Confirm-Cancel三个接口,灵活性最高但开发量大。本文对比AT和TCC两种模式的实现方式与适用场景。
Seata Server部署与注册中心配置
Seata Server(TC,事务协调者)是分布式事务的核心组件,负责管理全局事务和分支事务的状态。使用Docker部署Seata Server并注册到Nacos:
# docker-compose.yml
version: '3'
services:
seata-server:
image: seataio/seata-server:2.0.0
environment:
- SEATA_PORT=8091
- STORE_MODE=db
- SEATA_IP=192.168.1.100
volumes:
- ./seata-config/application.yml:/seata-server/resources/application.yml
ports:
- "8091:8091"
- "7091:7091"
Seata Server配置application.yml,使用Nacos作为注册中心和配置中心,MySQL存储事务日志:
# seata-config/application.yml
seata:
config:
type: nacos
nacos:
server-addr: 192.168.1.50:8848
group: SEATA_GROUP
namespace: seata
data-id: seataServer.properties
registry:
type: nacos
nacos:
server-addr: 192.168.1.50:8848
group: SEATA_GROUP
namespace: seata
cluster: default
store:
mode: db
db:
datasource: druid
db-type: mysql
url: jdbc:mysql://192.168.1.60:3306/seata?characterEncoding=utf8
user: root
password: password123
global-table: global_table
branch-table: branch_table
lock-table: lock_table
创建Seata所需的数据库表(global_table、branch_table、lock_table、distributed_lock),建表SQL在Seata源码script/server/db/mysql.sql中。
AT模式接入与自动补偿原理
AT模式(Automatic Transaction)通过拦截SQL执行,自动记录修改前后的数据快照(before image和after image),在事务回滚时根据before image自动生成反向SQL。业务代码只需添加@GlobalTransactional注解,无需编写补偿逻辑。
微服务接入AT模式配置(Spring Boot + Nacos):
# application.yml
seata:
enabled: true
application-id: order-service
tx-service-group: my_tx_group
service:
vgroup-mapping:
my_tx_group: default
registry:
type: nacos
nacos:
server-addr: 192.168.1.50:8848
group: SEATA_GROUP
namespace: seata
datasource:
auto-commit: false
# 每个微服务的数据库需要创建undo_log表
# CREATE TABLE undo_log (
# id BIGINT PRIMARY KEY AUTO_INCREMENT,
# branch_id BIGINT NOT NULL,
# xid VARCHAR(128) NOT NULL,
# context VARCHAR(128) NOT NULL,
# rollback_info LONGBLOB NOT NULL,
# log_status INT NOT NULL,
# log_created DATETIME NOT NULL,
# log_modified DATETIME NOT NULL,
# UNIQUE KEY ux_undo_log (xid, branch_id)
# )
业务代码使用@GlobalTransactional注解开启全局事务:
@Service
public class OrderService {
@Autowired
private OrderMapper orderMapper;
@Autowired
private AccountFeignClient accountFeignClient;
@Autowired
private StorageFeignClient storageFeignClient;
@GlobalTransactional(name = "createOrder", rollbackFor = Exception.class)
public void createOrder(OrderDTO dto) {
// 1. 创建订单(本地事务)
orderMapper.insert(dto.toEntity());
// 2. 扣减账户余额(远程调用,分支事务)
accountFeignClient.debit(dto.getUserId(), dto.getAmount());
// 3. 扣减库存(远程调用,分支事务)
storageFeignClient.deduct(dto.getProductId(), dto.getCount());
}
}
// 账户服务 - 分支事务
@RestController
@RequestMapping("/account")
public class AccountController {
@Autowired
private AccountMapper accountMapper;
@PostMapping("/debit")
public String debit(@RequestParam Long userId, @RequestParam BigDecimal amount) {
Account account = accountMapper.selectById(userId);
if (account.getBalance().compareTo(amount) < 0) {
throw new RuntimeException("余额不足");
}
accountMapper.updateBalance(userId, account.getBalance().subtract(amount));
return "success";
}
}
AT模式执行流程:TM(事务发起方)向TC注册全局事务获取XID → RM(资源管理器)拦截SQL,记录before/after image到undo_log表 → 业务执行 → TC检测到异常,通知各RM根据undo_log回滚 → RM执行反向SQL恢复数据。整个过程对业务代码透明。
TCC模式接入与手动补偿实现
TCC模式(Try-Confirm-Cancel)需要业务代码实现三个方法:Try预留资源、Confirm确认提交、Cancel回滚释放资源。TCC不依赖数据库事务,性能更高,适合非关系型数据源或跨异构数据源场景。
// 定义TCC接口,使用@LocalTCC注解
@LocalTCC
public interface AccountTccAction {
// Try阶段:冻结金额
@TwoPhaseBusinessAction(
name = "debitTcc",
commitMethod = "confirm",
rollbackMethod = "cancel"
)
boolean prepare(
@BusinessActionContextParameter(paramName = "userId") Long userId,
@BusinessActionContextParameter(paramName = "amount") BigDecimal amount
);
// Confirm阶段:扣除冻结金额
boolean confirm(BusinessActionContext context);
// Cancel阶段:解冻金额
boolean cancel(BusinessActionContext context);
}
// TCC实现
@Service
public class AccountTccActionImpl implements AccountTccAction {
@Autowired
private AccountMapper accountMapper;
@Autowired
private TccLogMapper tccLogMapper;
@Override
@Transactional
public boolean prepare(Long userId, BigDecimal amount) {
// 检查余额
Account account = accountMapper.selectById(userId);
if (account.getBalance().compareTo(amount) < 0) {
throw new RuntimeException("余额不足");
}
// 冻结金额:balance减少,frozen增加
accountMapper.freeze(userId, amount);
// 记录TCC日志,用于幂等和空回滚判断
TccLog log = new TccLog();
log.setXid(RootContext.getXID());
log.setBranchId(RootContext.getBranchId());
log.setUserId(userId);
log.setAmount(amount);
log.setStatus("trying");
tccLogMapper.insert(log);
return true;
}
@Override
@Transactional
public boolean confirm(BusinessActionContext context) {
Long userId = (Long) context.getActionContext("userId");
BigDecimal amount = (BigDecimal) context.getActionContext("amount");
String xid = context.getXid();
// 幂等检查:已确认则跳过
TccLog log = tccLogMapper.findByXid(xid);
if (log != null && "confirmed".equals(log.getStatus())) {
return true;
}
// 扣减冻结金额
accountMapper.deductFrozen(userId, amount);
log.setStatus("confirmed");
tccLogMapper.update(log);
return true;
}
@Override
@Transactional
public boolean cancel(BusinessActionContext context) {
Long userId = (Long) context.getActionContext("userId");
BigDecimal amount = (BigDecimal) context.getActionContext("amount");
String xid = context.getXid();
// 空回滚检查:Try未执行则直接返回
TccLog log = tccLogMapper.findByXid(xid);
if (log == null) {
return true;
}
// 幂等检查
if ("cancelled".equals(log.getStatus())) {
return true;
}
// 解冻金额:balance恢复,frozen减少
accountMapper.unfreeze(userId, amount);
log.setStatus("cancelled");
tccLogMapper.update(log);
return true;
}
}
AT与TCC模式对比与选型
AT模式接入成本最低,只需添加注解和undo_log表,适合关系型数据库的简单CRUD场景。AT模式在提交阶段依赖全局锁保证隔离性,高并发写入场景下性能有瓶颈。
TCC模式需要手动实现三个接口,开发量大,但不依赖数据库事务和undo_log,性能更高。TCC适合高并发、跨异构数据源(如Redis+MySQL)或非标准SQL操作的场景。
TCC模式需要处理三个关键问题:幂等性(Confirm和Cancel可能被重复调用)、空回滚(Try未执行但Cancel被调用)、悬挂(Cancel先于Try执行)。通过维护TCC日志表记录每个分支事务的XID和状态来解决这三个问题。
SAGA模式适合长事务场景(如涉及外部系统调用、人工审批),通过状态机驱动正反向操作。XA模式依赖数据库XA协议,性能最低但强一致性最好,适合金融核心场景。
原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/fen-bu-shi-shi-wu-shi-zhan-seataat-mo-shi-yu-tcc-bu-chang/