seata分布式事务解决方案
最成熟模式:AT 和 TCC 是 Seata 中应用最广泛、稳定性最高的模式,尤其 AT 模式因其无侵入性成为 Java 微服务的首选。场景适配:追求开发效率 → AT 模式;需要高性能补偿 → TCC 模式;长事务最终一致 → SAGA 模式;强一致性刚性需求 → XA 模式。未来趋势:Seata 持续优化 AT 模式的锁机制(如异步化提交),并增强多语言支持(如通过 Sideca
最成熟模式:AT 和 TCC 是 Seata 中应用最广泛、稳定性最高的模式,尤其 AT 模式因其无侵入性成为 Java 微服务的首选。
场景适配:
追求开发效率 → AT 模式;
需要高性能补偿 → TCC 模式;
长事务最终一致 → SAGA 模式;
强一致性刚性需求 → XA 模式。
未来趋势:Seata 持续优化 AT 模式的锁机制(如异步化提交),并增强多语言支持(如通过 Sidecar 代理非 Java 服务)。
建议结合业务一致性要求、性能压力和团队技术栈综合选型,优先验证 AT 或 TCC 模式,再逐步扩展复杂场景支持。
Tcc
TCC核心价值:通过业务层资源预留(冻结库存/订单/优惠券)替代数据库锁,提升并发性能。
必做保障:幂等性、空回滚处理、防悬挂是TCC可靠性的基石。
适用场景:高并发短事务(如电商下单、支付)。
基于tcc完整输出一个案例
以下是一个基于TCC模式的完整电商下单案例,涵盖库存冻结、订单创建、优惠券核销三个服务,并处理分布式事务中的幂等性、空回滚、悬挂问题。
📦 案例场景
用户下单流程涉及三个服务:
- 库存服务:冻结/扣减商品库存
- 订单服务:创建/确认订单
- 优惠券服务:锁定/核销优惠券
🛠️ 数据库设计
– 库存表
CREATE TABLE inventory (
id BIGINT PRIMARY KEY,
product_id BIGINT UNIQUE,
total INT NOT NULL, – 总库存
available INT NOT NULL, – 可用库存
frozen INT NOT NULL – 冻结库存
);
– 订单表
CREATE TABLE orders (
id VARCHAR(64) PRIMARY KEY,
user_id BIGINT,
status VARCHAR(20), – TRYING/CONFIRMED/CANCELLED
items JSON
);
– 优惠券表
CREATE TABLE coupon (
id BIGINT PRIMARY KEY,
user_id BIGINT,
code VARCHAR(32),
status VARCHAR(20) – LOCKED/USED/FREE
);
– 事务日志表(幂等性保障)
CREATE TABLE tcc_log (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
xid VARCHAR(128) NOT NULL, – 全局事务ID
service_name VARCHAR(50) NOT NULL,
status VARCHAR(20) NOT NULL – TRY/CONFIRM/CANCEL
);
⚙️ TCC接口实现
- 库存服务(InventoryService)
@LocalTCC
public interface InventoryTccService {
@TwoPhaseBusinessAction(name = "freeze", commitMethod = "confirmDeduct", rollbackMethod = "cancelDeduct")
boolean tryFreeze(@BusinessActionContextParameter("productId") Long productId,
@BusinessActionContextParameter("quantity") Integer quantity,
@BusinessActionContextParameter("xid") String xid);
boolean confirmDeduct(BusinessActionContext context);
boolean cancelDeduct(BusinessActionContext context);
}
@Service
public class InventoryTccServiceImpl implements InventoryTccService {
@Autowired
private InventoryMapper inventoryMapper;
@Autowired
private TccLogMapper tccLogMapper;
@Transactional
public boolean tryFreeze(Long productId, Integer quantity, String xid) {
// 防悬挂:检查是否已执行Cancel
if (tccLogMapper.existsByXidAndStatus(xid, "CANCEL")) {
return false;
}
// 幂等性:避免重复Try
if (tccLogMapper.existsByXidAndStatus(xid, "TRY")) {
return true;
}
// 业务操作:冻结库存
inventoryMapper.freezeInventory(productId, quantity);
// 记录事务状态
tccLogMapper.log(xid, "inventory", "TRY");
}
@Transactional
public boolean confirmDeduct(BusinessActionContext context) {
String xid = context.getXid();
// 幂等性检查
if (tccLogMapper.existsByXidAndStatus(xid, "CONFIRM")) return true;
// 执行业务:扣减冻结库存
Long productId = context.getActionContext("productId");
Integer quantity = context.getActionContext("quantity");
inventoryMapper.confirmDeduct(productId, quantity);
tccLogMapper.log(xid, "inventory", "CONFIRM");
return true;
}
@Transactional
public boolean cancelDeduct(BusinessActionContext context) {
String xid = context.getXid();
// 空回滚检查:未执行Try则直接返回
if (!tccLogMapper.existsByXidAndStatus(xid, "TRY")) {
tccLogMapper.log(xid, "inventory", "CANCEL"); // 记录空回滚
return true;
}
// 幂等性检查
if (tccLogMapper.existsByXidAndStatus(xid, "CANCEL")) return true;
// 业务操作:解冻库存
Long productId = context.getActionContext("productId");
Integer quantity = context.getActionContext("quantity");
inventoryMapper.cancelDeduct(productId, quantity);
tccLogMapper.log(xid, "inventory", "CANCEL");
return true;
}
}
- 订单服务(OrderService)
@LocalTCC
public interface OrderTccService {
@TwoPhaseBusinessAction(name = “create”, commitMethod = “confirm”, rollbackMethod = “cancel”)
boolean tryCreate(@BusinessActionContextParameter(“order”) Order order,
@BusinessActionContextParameter(“xid”) String xid);
boolean confirm(BusinessActionContext context);
boolean cancel(BusinessActionContext context);
}
@Service
public class OrderTccServiceImpl implements OrderTccService {
@Autowired
private OrderMapper orderMapper;
@Autowired
private TccLogMapper tccLogMapper;
@Transactional
public boolean tryCreate(Order order, String xid) {
if (tccLogMapper.existsByXidAndStatus(xid, "CANCEL")) return false;
if (tccLogMapper.existsByXidAndStatus(xid, "TRY")) return true;
// 创建预订单(状态为TRYING)
order.setStatus("TRYING");
orderMapper.insert(order);
tccLogMapper.log(xid, "order", "TRY");
return true;
}
@Transactional
public boolean confirm(BusinessActionContext context) {
String xid = context.getXid();
if (tccLogMapper.existsByXidAndStatus(xid, "CONFIRM")) return true;
// 更新订单状态为CONFIRMED
Order order = context.getActionContext("order");
orderMapper.updateStatus(order.getId(), "CONFIRMED");
tccLogMapper.log(xid, "order", "CONFIRM");
return true;
}
// Cancel逻辑类似(略)
}
- 优惠券服务(CouponService)
@LocalTCC
public interface CouponTccService {
@TwoPhaseBusinessAction(name = “lock”, commitMethod = “confirmUse”, rollbackMethod = “cancelUse”)
boolean tryLock(@BusinessActionContextParameter(“couponId”) Long couponId,
@BusinessActionContextParameter(“xid”) String xid);
boolean confirmUse(BusinessActionContext context);
boolean cancelUse(BusinessActionContext context);
}
@Service
public class CouponTccServiceImpl implements CouponTccService {
@Autowired
private CouponMapper couponMapper;
@Autowired
private TccLogMapper tccLogMapper;
@Transactional
public boolean tryLock(Long couponId, String xid) {
if (tccLogMapper.existsByXidAndStatus(xid, "CANCEL")) return false;
if (tccLogMapper.existsByXidAndStatus(xid, "TRY")) return true;
// 锁定优惠券(状态为LOCKED)
couponMapper.updateStatus(couponId, "LOCKED");
tccLogMapper.log(xid, "coupon", "TRY");
return true;
}
@Transactional
public boolean confirmUse(BusinessActionContext context) {
String xid = context.getXid();
if (tccLogMapper.existsByXidAndStatus(xid, "CONFIRM")) return true;
// 核销优惠券(状态为USED)
Long couponId = context.getActionContext("couponId");
couponMapper.updateStatus(couponId, "USED");
tccLogMapper.log(xid, "coupon", "CONFIRM");
return true;
}
// Cancel逻辑类似(略)
}
🔗 事务协调器(全局事务入口)
@Service
public class OrderCoordinator {
@Autowired
private InventoryTccService inventoryTccService;
@Autowired
private OrderTccService orderTccService;
@Autowired
private CouponTccService couponTccService;
@GlobalTransactional
public void createOrder(Order order, Long productId, Integer quantity, Long couponId) {
String xid = RootContext.getXID(); // 获取全局事务ID
// ==== Try阶段 ====
if (!inventoryTccService.tryFreeze(productId, quantity, xid))
throw new RuntimeException("库存冻结失败");
if (!orderTccService.tryCreate(order, xid))
throw new RuntimeException("订单创建失败");
if (!couponTccService.tryLock(couponId, xid))
throw new RuntimeException("优惠券锁定失败");
// ==== Confirm阶段(全部Try成功)====
inventoryTccService.confirmDeduct(new BusinessActionContext(xid));
orderTccService.confirm(new BusinessActionContext(xid));
couponTccService.confirmUse(new BusinessActionContext(xid));
}
}
⚠️ 异常处理设计
-
空回滚
• 在Cancel方法中检查事务日志:若未执行Try,则记录空回滚状态并返回。 -
幂等性
• 通过事务日志表tcc_log记录每个服务的执行状态,重复调用时直接返回成功。 -
悬挂问题
• Try操作前检查事务日志:若已执行Cancel,则拒绝执行Try。
🔄 事务执行流程
-
正常流程

-
回滚流程(Try阶段失败)

💎 总结
• TCC核心价值:通过业务层资源预留(冻结库存/订单/优惠券)替代数据库锁,提升并发性能。
• 必做保障:幂等性、空回滚处理、防悬挂是TCC可靠性的基石。
• 适用场景:高并发短事务(如电商下单、支付)。
完整代码参考:https://github.com/seata/seata-samples | https://seata.io/zh-cn/blog/tcc-mode-exception-handling.html
seata的tcc模式除了用于跨库,是否可用于单库呢?
是的,Seata 的 TCC 模式完全可用于单库场景,且在某些业务需求下具有独特优势。尽管 TCC 常被强调适用于跨库或跨服务事务,但其设计本质并不依赖底层资源是否分布式。以下是详细分析:
⚙️ 一、TCC 模式在单库场景的应用价值
- 精细化业务控制
• 业务逻辑隔离
在单库中,若需对同一数据的不同操作(如库存冻结与扣减)进行分离,TCC 可将它们拆分为独立的 Try、Confirm/Cancel 阶段,避免耦合。
示例:库存冻结在 Try 阶段执行,实际扣减在 Confirm 阶段完成,解冻在 Cancel 阶段处理。
• 复杂事务编排
单库内需协调多个关联操作时(如订单创建 → 库存冻结 → 支付预占),TCC 提供显式的事务阶段划分,比传统本地事务更灵活。
- 高并发与性能优化
• 减少锁竞争时间
传统事务中,锁定资源需持续至事务结束。而 TCC 的 Try 阶段仅预留资源(如冻结库存),Confirm 阶段快速提交,大幅缩短锁持有时间,提升并发能力。
• 异步化提交
Confirm/Cancel 阶段可异步执行,避免阻塞主业务流程,适用于高并发场景(如秒杀库存扣减)。
- 一致性保障升级
• 强隔离性需求
在单库中,若业务要求避免脏读(如账户余额变更),TCC 通过预留资源(Try 阶段)实现类似“乐观锁”的效果,优于 AT 模式的读未提交隔离级别。
• 复杂回滚逻辑
需自定义回滚操作时(如释放冻结库存后记录日志),TCC 的 Cancel 阶段可灵活实现,而传统事务回滚仅依赖 SQL 反向操作。
⚖️ 三、TCC vs AT 模式在单库中的对比
维度 TCC 模式 AT 模式 适用场景建议

💡 性能数据参考:某电商库存系统在单库中使用 TCC 后,锁冲突率下降 60%,TPS 提升 3 倍。
⚠️ 四、单库使用 TCC 的注意事项
-
避免过度设计
若业务仅需原子性(如插入订单+更新库存),本地事务 或 AT 模式 更简洁高效。 -
解决 TCC 固有问题
• 空回滚:Cancel 时需检查 Try 是否已执行(如通过 tcc_fence_log 表记录状态)。• 幂等性:Confirm/Cancel 需支持重复调用(如用事务 ID 去重)。
-
事务日志管理
单库场景仍需 Seata TC 协调全局事务,需部署 TC 服务并配置事务组。
💎 总结
Seata TCC 不仅适用于跨库,在单库中同样具有价值,尤其在需要以下特性时:
- 业务操作解耦(如冻结 → 扣减分离)。
- 高并发与短锁优化(如秒杀库存)。
- 强隔离性与自定义回滚(如金融账户变更)。
推荐组合策略:简单操作用 AT 模式(如订单创建),核心资源操作(库存、余额)用 TCC 模式,兼顾效率与可控性。
tcc的问题
1、因为tcc的取消操作涉及到跨库多个系统,如果其中有2个数据库挂了,那就无法正常回滚了。
2、调第三方服务,如果所有服务都实现了TCC则,比如核心系统也实现了TCC,那么银行前置也实现TCC,那么就不存在点核心超时还要补偿的事情了
3、tcc联机调第三方的耗时服务,第三方的耗时服务如果不支持tcc或不在当前事务组中,则无法使用tcc.
三、Cancel阶段的异常处理挑战
重复失败的风险
若Cancel自身因网络问题失败,TC会自动重试,但重试时仍无法获取历史异常。
解决方案:
设计幂等Cancel,通过事务状态表避免重复操作。
多次重试失败后触发人工干预,依赖日志定位问题。
资源悬挂的预防
若Try在Cancel之后执行(网络延迟),需通过事务状态表防悬挂:Cancel阶段插入ROLLBACKED记录,Try执行前校验该记录
即使Cancel阶段遭遇多数据库故障,TCC事务仍可通过 “框架重试 + 业务降级 + 对账兜底” 三层机制保障最终一致性。核心要点在于:
TCC框架需实现高可用事务日志和智能重试;
业务层设计分段式冲正接口和超时降级策略;
最终通过自动化对账弥补系统性故障的缺口。
更多推荐
所有评论(0)