01. 什么是分布式事务?

在单体系统中,一次业务操作通常只涉及一个数据库节点,本地事务即可通过 ACID(原子性、一致性、隔离性、持久性)确保数据正确性。但随着系统拆分为微服务、数据分片、读写分离、多数据库架构等分布式模式,同一个业务操作往往涉及多个节点的资源操作。这时,本地事务不再能够保证全局一致性,就需要“分布式事务”。

分布式事务(Distributed Transaction)是指事务的参与者、资源、资源管理器与事务协调器分布在不同节点上,但整体仍需保证事务的原子性与一致性。

典型例子:跨银行转账

假设账户 A 在银行 X,账户 B 在银行 Y,需要完成转账:

  • 银行 X 扣款 A – 100

  • 银行 Y 加款 B + 100

两个操作不在同一数据库中,不能使用单节点本地事务保证一致性。一旦任意一方成功而另一方失败,数据就不一致。

为解决这种问题,就需要分布式事务协议。

02. 分布式事务的主要模型

常见的分布式模型有二阶段提交(2PC/XA)、三阶段提交(3PC)和SAGA。

2PC(二阶段提交,XA)

  • 传统、强一致性
  • 有协调器
  • 涉及 Prepare → Commit 两个阶段
  • 常用于数据库层面的强一致事务

3PC(三阶段提交)

  • 2PC 的改进版
  • 加入超时机制与预提交
  • 理论价值大于工程价值(几乎没有实际系统采用)

SAGA(长事务 / 补偿事务)

  • 无锁、最终一致
  • 常用于微服务
  • 每一步强调“可补偿操作”
  • 分为 编排式协同式 两种模式

03. 二阶段提交(2PC/XA)

2PC 是最典型的强一致性分布式事务协议,它由协调器(Coordinator)统一控制所有参与者(Participants)的提交过程。2PC的执行过程被分为投票和提交两个阶段:

  1. 投票阶段:协调者向所有参与者发送事务请求,询问他们是否可以提交事务。每个参与者预先执行事务,并根据本地情况决定是否投票。
  2. 提交/回滚阶段:如果所有参与者在前一阶段都回复 “是”,协调者就会向所有参与者发送 “提交 “命令。如果任何参与方回复 “否”,或在规定时间内没有回复,协调者将向所有参与者发送 “回滚 “命令。

2PC 的优缺点

优点强一致性、模型简单、数据库支持广泛
缺点协调器单点故障、参与者可能“悬挂事务”、锁资源时间长、性能下降

2PC 仍然是传统企业领域(金融、电信)常见的强一致性方案,也是 XA 规范的底层基础。

04. 使用2PC实现一个简单的分布式事务

下面是一个基于2PC模型使用 PostgreSQL 原生 PREPARE TRANSACTION 模拟上面的转账场景的Java代码示例:

				
					import java.sql.*;
import java.util.UUID;

public class Postgres2PCDemo {

    public static void main(String[] args) throws Exception {

        String url1 = "jdbc:postgresql://localhost:5432/db1";
        String url2 = "jdbc:postgresql://localhost:5432/db2";

        Connection c1 = DriverManager.getConnection(url1, "user1", "pass1");
        Connection c2 = DriverManager.getConnection(url2, "user2", "pass2");

        c1.setAutoCommit(false);
        c2.setAutoCommit(false);

        String xid = UUID.randomUUID().toString();

        try {
            // 参与者1:扣款
            c1.createStatement().executeUpdate(
                "UPDATE account SET balance = balance - 100 WHERE id = 1"
            );

            // 参与者2:加款
            c2.createStatement().executeUpdate(
                "UPDATE account SET balance = balance + 100 WHERE id = 2"
            );

            // 阶段1:Prepare
            c1.createStatement().execute("PREPARE TRANSACTION '" + xid + "-p1'");
            c2.createStatement().execute("PREPARE TRANSACTION '" + xid + "-p2'");

            // 阶段2:Commit(协调器决策成功)
            c1.createStatement().execute("COMMIT PREPARED '" + xid + "-p1'");
            c2.createStatement().execute("COMMIT PREPARED '" + xid + "-p2'");

            System.out.println("Global 2PC commit success!");

        } catch (Exception e) {
            System.out.println("Error, global rollback");

            // 回滚已 prepare 的事务
            try {
                c1.createStatement().execute("ROLLBACK PREPARED '" + xid + "-p1'");
            } catch (Exception ignore) {}
            try {
                c2.createStatement().execute("ROLLBACK PREPARED '" + xid + "-p2'");
            } catch (Exception ignore) {}

            throw e;
        }
    }
}