【架构师之路】TCC 分布式事务解决方案与实现

TCC 分布式事务解决方案介绍

分布式系统中,保证数据操作的一致性是一个重要的挑战。TCC(Try-Confirm-Cancel)是一种分布式事务解决方案,通过将分布式事务分解为三个阶段(尝试确认取消)来确保事务的一致性和可靠性。

TCC 解决方案的工作原理

在这里插入图片描述

  1. Try 阶段

    • 在 Try 阶段,业务系统尝试执行事务操作,并预留必要的资源
    • 如果所有参与者都成功执行 Try 阶段,事务进入 Confirm 阶段。如果有任何一个参与者失败,则执行 Cancel 阶段。
  2. Confirm 阶段

    • 在 Confirm 阶段,业务系统确认执行事务操作,并提交事务。在这个阶段,所有资源的锁都会被释放。
    • 如果 Confirm 阶段中的任何一个参与者失败,系统需要执行 Cancel 阶段。
  3. Cancel 阶段

    • 在 Cancel 阶段,业务系统撤销事务操作,并释放之前的资源。这个阶段用于回滚之前尝试过的操作,以保证数据的一致性。

场景案例

下面我们通过创建订单减库存这样一个案例来演示下TCC的过程。

// 全局事务上下文
// 作用:记录全局事务状态,供所有参与者共享
public class TccContext {
    private String xid;          // 全局事务ID
    private boolean committed;  // 事务提交状态
    
    public TccContext(String xid) { this.xid = xid; }
    
    public synchronized void commit() { 
        this.committed = true; 
    }
    public boolean isCommitted() { 
        return committed; 
    }
}

// TCC 参与者接口
public interface TccParticipant {
    // Try阶段:资源预留(如冻结库存)
    void tryExecute(TccContext context) throws Exception;
    // Confirm阶段:提交资源(如扣减库存)
    void confirm(TccContext context);
    // Cancel阶段:回滚资源(如释放库存)
    void cancel(TccContext context);
}

参与者实现示例(订单与库存服务)

设计要点:

  • Try 阶段仅执行资源预留,不实际修改业务数据
  • Confirm/Cancel 操作需保证幂等性(如通过事务日志去重)
// 订单服务参与者
public class OrderParticipant implements TccParticipant {
    private String orderId;
    
    public OrderParticipant(String orderId) { 
        this.orderId = orderId; 
    }

    @Override
    public void tryExecute(TccContext context) throws Exception {
        System.out.println("订单[" + orderId + "] Try: 创建预订单");
        // 实际业务:插入预订单记录(状态为"预创建")
    }

    @Override
    public void confirm(TccContext context) {
        if (context.isCommitted()) {
            System.out.println("订单[" + orderId + "] Confirm: 正式提交订单");
            // 实际业务:更新订单状态为"已创建"
        }
    }

    @Override
    public void cancel(TccContext context) {
        System.out.println("订单[" + orderId + "] Cancel: 删除预订单");
        // 实际业务:删除预订单记录
    }
}


//  库存服务参与者
public class InventoryParticipant implements TccParticipant {
    private String productId;
    private int quantity;

    public InventoryParticipant(String productId, int quantity) {
        this.productId = productId;
        this.quantity = quantity;
    }

    @Override
    public void tryExecute(TccContext context) throws Exception {
        System.out.println("库存[" + productId + "] Try: 冻结" + quantity + "件");
        // 实际业务:冻结库存(如 update stock set frozen = frozen + ? where product_id = ?)
    }

    @Override
    public void confirm(TccContext context) {
        if (context.isCommitted()) {
            System.out.println("库存[" + productId + "] Confirm: 扣减" + quantity + "件");
            // 实际业务:扣减已冻结库存(update stock set total = total - ? where product_id = ?)
        }
    }

    @Override
    public void cancel(TccContext context) {
        System.out.println("库存[" + productId + "] Cancel: 释放" + quantity + "件");
        // 实际业务:释放冻结库存(update stock set frozen = frozen - ? where product_id = ?)
    }
}


事务协调者

  • 所有参与者 Try 成功后才执行 Confirm
  • 任一 Try 失败立即触发 Cancel
public class TccCoordinator {
    private List<TccParticipant> participants;
    private TccContext context;

    public TccCoordinator(List<TccParticipant> participants, String xid) {
        this.participants = participants;
        this.context = new TccContext(xid);
    }

    public void execute() {
        try {
            // 阶段1:Try 资源预留
            for (TccParticipant p : participants) {
                p.tryExecute(context);
            }
            // 阶段2:Confirm 提交(假设业务逻辑通过)
            context.commit();
            for (TccParticipant p : participants) {
                p.confirm(context);
            }
        } catch (Exception e) {
            // 阶段3:Cancel 回滚
            for (TccParticipant p : participants) {
                p.cancel(context);
            }
            throw new RuntimeException("事务回滚: " + e.getMessage());
        }
    }
}

场景模拟

public class TccDemo {
    public static void main(String[] args) {
        // 创建参与者(订单ID=O1001,商品ID=P2001,数量2件)
        TccParticipant order = new OrderParticipant("O1001");
        TccParticipant inventory = new InventoryParticipant("P2001", 2);
        
        // 事务协调(全局事务ID=TX202503121621)
        TccCoordinator coordinator = new TccCoordinator(
            List.of(order, inventory), 
            "TX202503121621"
        );
        
        // 执行事务
        try {
            coordinator.execute();
            System.out.println("事务提交成功");
        } catch (Exception e) {
            System.out.println("事务失败: " + e.getMessage());
        }
    }
}


成功场景

订单[O1001] Try: 创建预订单
库存[P2001] Try: 冻结2件
订单[O1001] Confirm: 正式提交订单
库存[P2001] Confirm: 扣减2件
事务提交成功

失败场景(假设库存不足)

订单[O1001] Try: 创建预订单
库存[P2001] Try: 冻结2件 → 抛出异常(库存不足)
订单[O1001] Cancel: 删除预订单
库存[P2001] Cancel: 释放2件
事务失败: 事务回滚: 库存不足

TCC 的优势和适用场景

  • 数据一致性:TCC 可以确保分布式事务的数据一致性,即使在出现部分故障的情况下也能够正确处理事务。
  • 灵活性:TCC 允许业务系统在 Confirm 和 Cancel 阶段执行自定义的逻辑,使其更加灵活适用于不同的业务场景。
  • 性能:相比于传统的两阶段提交(2PC)协议,TCC 在性能上有一定的优势,尤其在高并发场景下表现更好。

解决方案

目前市面上的TCC框架众多比如下面这几种:

框架名称Gitbub地址
tcc-transactionhttps://github.com/changmingxie/tcc-transaction
Hmilyhttps://github.com/yu199195/hmily
ByteTCChttps://github.com/liuyangming/ByteTCC
EasyTransactionhttps://github.com/QNJR-GROUP/EasyTransaction

总结

TCC 是一种灵活而高效的分布式事务解决方案,通过将事务分解为 Try、Confirm 和 Cancel 阶段,确保了事务的一致性和可靠性。在设计分布式系统时,考虑使用 TCC 可能是一个不错的选择,特别是在需要更高性能和更灵活的事务管理方式时。


在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值