一、什么是分布式事务?

想象一下,你去银行转账:从A账户转100元到B账户。这个操作需要两步:A账户扣钱,B账户加钱。如果这两步中任何一步失败,整个转账就失败了。在单机系统中,数据库事务可以保证这两步要么都成功,要么都失败。

但在分布式系统中,A账户和B账户可能在不同的服务器上,甚至在不同的数据库里。这时候,传统的单机事务就不管用了,我们需要分布式事务来保证多个服务之间的数据一致性。

二、2PC:最经典的分布式事务方案

2PC(Two-Phase Commit,两阶段提交)是最早的分布式事务解决方案之一。它的核心思想是:先问,再执行

2PC 的工作流程

  1. 准备阶段(Prepare Phase):协调者(Coordinator)询问所有参与者(Participants):“你们能执行这个操作吗?”
  2. 提交阶段(Commit Phase):如果所有参与者都回答“可以”,协调者就通知大家“正式执行”;如果有任何一个参与者说“不行”,协调者就让大家“全部回滚”。

示例(基于Java + MySQL)

// 模拟2PC的协调者
public class TwoPhaseCommit {
    public boolean transfer(Account from, Account to, int amount) {
        // 第一阶段:询问所有参与者是否可以执行
        boolean canCommit = true;
        try {
            canCommit &= from.prepareDebit(amount); // A账户准备扣款
            canCommit &= to.prepareCredit(amount);  // B账户准备收款
        } catch (Exception e) {
            canCommit = false;
        }

        // 第二阶段:根据第一阶段的反馈决定提交或回滚
        if (canCommit) {
            from.commit();  // A账户正式扣款
            to.commit();    // B账户正式收款
            return true;
        } else {
            from.rollback(); // A账户回滚
            to.rollback();   // B账户回滚
            return false;
        }
    }
}

// 模拟账户参与者
class Account {
    boolean prepareDebit(int amount) {
        // 检查余额是否足够
        if (this.balance >= amount) {
            this.tempHold = amount; // 临时冻结金额
            return true;
        }
        return false;
    }

    void commit() {
        this.balance -= this.tempHold; // 正式扣款
        this.tempHold = 0;
    }

    void rollback() {
        this.tempHold = 0; // 解冻金额
    }
}

2PC 的优缺点

优点

  • 实现简单,容易理解。
  • 强一致性,适合对数据一致性要求高的场景(如金融系统)。

缺点

  • 同步阻塞:所有参与者必须等待协调者的指令,如果协调者挂了,整个系统可能卡住。
  • 单点故障:协调者一旦宕机,事务可能无法完成。
  • 性能较低:需要多次网络通信,不适合高并发场景。

三、TCC:更灵活的分布式事务方案

TCC(Try-Confirm-Cancel)是另一种分布式事务方案,它的核心思想是:先预留资源,再确认执行

TCC 的工作流程

  1. Try 阶段:尝试执行,预留资源(比如先冻结金额)。
  2. Confirm 阶段:如果所有Try都成功,就正式提交。
  3. Cancel 阶段:如果任何Try失败,就取消预留的资源。

示例(基于Java + MySQL)

// 模拟TCC事务
public class TCCTransfer {
    public boolean transfer(Account from, Account to, int amount) {
        // Try阶段:预留资源
        boolean trySuccess = true;
        try {
            trySuccess &= from.tryDebit(amount); // A账户冻结金额
            trySuccess &= to.tryCredit(amount);  // B账户预留收款
        } catch (Exception e) {
            trySuccess = false;
        }

        // 根据Try阶段的结果决定Confirm或Cancel
        if (trySuccess) {
            from.confirm(); // A账户正式扣款
            to.confirm();   // B账户正式收款
            return true;
        } else {
            from.cancel();  // A账户解冻金额
            to.cancel();    // B账户取消预留
            return false;
        }
    }
}

// 模拟TCC账户
class Account {
    boolean tryDebit(int amount) {
        if (this.balance >= amount) {
            this.frozenAmount = amount; // 冻结金额
            return true;
        }
        return false;
    }

    void confirm() {
        this.balance -= this.frozenAmount; // 正式扣款
        this.frozenAmount = 0;
    }

    void cancel() {
        this.frozenAmount = 0; // 解冻金额
    }
}

TCC 的优缺点

优点

  • 异步化:Try阶段成功后,Confirm/Cancel可以异步执行,提高性能。
  • 无阻塞:即使协调者挂了,参与者可以自己超时后执行Cancel。
  • 适合高并发:比2PC更轻量,适合互联网业务(如电商、秒杀)。

缺点

  • 业务侵入性强:需要手动编写Try/Confirm/Cancel逻辑。
  • 数据一致性稍弱:如果Confirm/Cancel失败,可能需要人工干预。

四、如何选择?2PC 还是 TCC?

对比项 2PC TCC
一致性 强一致性(适合金融) 最终一致性(适合互联网)
性能 较低(同步阻塞) 较高(异步执行)
复杂度 简单(数据库层面支持) 复杂(需要业务代码配合)
适用场景 传统企业级系统(如银行) 高并发互联网业务(如电商)

实际场景建议

  1. 金融支付:用2PC,因为数据不能出错。
  2. 电商下单:用TCC,因为要支持高并发。
  3. 混合方案:有些系统会结合两种方案,比如核心交易用2PC,非核心用TCC。

五、总结

分布式事务没有银弹,2PC和TCC各有优劣:

  • 2PC 简单但性能低,适合强一致性场景。
  • TCC 灵活但实现复杂,适合高并发业务。

选择时,要根据业务需求权衡一致性性能