最成熟模式​:​AT 和 TCC​ 是 Seata 中应用最广泛、稳定性最高的模式,尤其 AT 模式因其无侵入性成为 Java 微服务的首选。
​场景适配​:
追求开发效率 → AT 模式;
需要高性能补偿 → TCC 模式;
长事务最终一致 → SAGA 模式;
强一致性刚性需求 → XA 模式。
​未来趋势​:Seata 持续优化 AT 模式的锁机制(如异步化提交),并增强多语言支持(如通过 Sidecar 代理非 Java 服务)。
建议结合业务一致性要求、性能压力和团队技术栈综合选型,优先验证 AT 或 TCC 模式,再逐步扩展复杂场景支持。
在这里插入图片描述

Tcc

​TCC核心价值​:通过业务层资源预留(冻结库存/订单/优惠券)替代数据库锁,提升并发性能。
​必做保障​:幂等性、空回滚处理、防悬挂是TCC可靠性的基石。
​适用场景​:高并发短事务(如电商下单、支付)。

基于tcc完整输出一个案例

以下是一个基于TCC模式的完整电商下单案例,涵盖库存冻结、订单创建、优惠券核销三个服务,并处理分布式事务中的幂等性、空回滚、悬挂问题。

📦 案例场景

用户下单流程涉及三个服务:

  1. 库存服务:冻结/扣减商品库存
  2. 订单服务:创建/确认订单
  3. 优惠券服务:锁定/核销优惠券

🛠️ 数据库设计

– 库存表
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接口实现

  1. 库存服务(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;
}

}

  1. 订单服务(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逻辑类似(略)

}

  1. 优惠券服务(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));
}

}

⚠️ 异常处理设计

  1. 空回滚
    • 在Cancel方法中检查事务日志:若未执行Try,则记录空回滚状态并返回。

  2. 幂等性
    • 通过事务日志表tcc_log记录每个服务的执行状态,重复调用时直接返回成功。

  3. 悬挂问题
    • Try操作前检查事务日志:若已执行Cancel,则拒绝执行Try。

🔄 事务执行流程

  1. 正常流程
    在这里插入图片描述

  2. 回滚流程(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 模式在单库场景的应用价值

  1. 精细化业务控制

• 业务逻辑隔离

在单库中,若需对同一数据的不同操作(如库存冻结与扣减)进行分离,TCC 可将它们拆分为独立的 Try、Confirm/Cancel 阶段,避免耦合。
示例:库存冻结在 Try 阶段执行,实际扣减在 Confirm 阶段完成,解冻在 Cancel 阶段处理。
• 复杂事务编排

单库内需协调多个关联操作时(如订单创建 → 库存冻结 → 支付预占),TCC 提供显式的事务阶段划分,比传统本地事务更灵活。

  1. 高并发与性能优化

• 减少锁竞争时间

传统事务中,锁定资源需持续至事务结束。而 TCC 的 Try 阶段仅预留资源(如冻结库存),Confirm 阶段快速提交,大幅缩短锁持有时间,提升并发能力。
• 异步化提交

Confirm/Cancel 阶段可异步执行,避免阻塞主业务流程,适用于高并发场景(如秒杀库存扣减)。

  1. 一致性保障升级

• 强隔离性需求

在单库中,若业务要求避免脏读(如账户余额变更),TCC 通过预留资源(Try 阶段)实现类似“乐观锁”的效果,优于 AT 模式的读未提交隔离级别。
• 复杂回滚逻辑

需自定义回滚操作时(如释放冻结库存后记录日志),TCC 的 Cancel 阶段可灵活实现,而传统事务回滚仅依赖 SQL 反向操作。

⚖️ 三、TCC vs AT 模式在单库中的对比

维度 TCC 模式 AT 模式 适用场景建议

在这里插入图片描述

💡 性能数据参考:某电商库存系统在单库中使用 TCC 后,锁冲突率下降 60%,TPS 提升 3 倍。

⚠️ 四、单库使用 TCC 的注意事项

  1. 避免过度设计
    若业务仅需原子性(如插入订单+更新库存),本地事务 或 AT 模式 更简洁高效。

  2. 解决 TCC 固有问题
    • 空回滚:Cancel 时需检查 Try 是否已执行(如通过 tcc_fence_log 表记录状态)。

    • 幂等性:Confirm/Cancel 需支持重复调用(如用事务 ID 去重)。

  3. 事务日志管理
    单库场景仍需 Seata TC 协调全局事务,需部署 TC 服务并配置事务组。

💎 总结

Seata TCC 不仅适用于跨库,在单库中同样具有价值,尤其在需要以下特性时:

  1. 业务操作解耦(如冻结 → 扣减分离)。
  2. 高并发与短锁优化(如秒杀库存)。
  3. 强隔离性与自定义回滚(如金融账户变更)。

推荐组合策略:简单操作用 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框架需实现高可用事务日志和智能重试;
业务层设计分段式冲正接口和超时降级策略;
最终通过自动化对账弥补系统性故障的缺口。

Logo

更多推荐