分布式事务-2pc

分布式事务在电商交易系统中经常会遇到,其中一个实现方案两阶段提交(简称“2pc”)比较经典,今天心血来潮利用DeepSeek辅助手写了一把

什么是分布式事务?

分布式事务是指涉及多个独立资源(如数据库、消息队列、缓存等)的事务操作,需要保证所有资源要么全部提交成功,要么全部回滚,以确保数据的一致性。

什么是 2PC(两阶段提交)

2PC 是一种分布式事务协议,分为两个阶段:

  1. 准备阶段(Prepare Phase):
    ● 协调者询问所有参与者是否可以提交事务。
    ● 参与者执行事务操作,并返回准备结果(成功或失败)。
  2. 提交阶段(Commit Phase):
    ● 如果所有参与者都准备成功,协调者发送提交请求。
    ● 如果任一参与者准备失败,协调者发送回滚请求。

2PC 的核心组件

● 协调者(Coordinator):负责协调事务的提交或回滚。
● 参与者(Participant):负责执行具体的事务操作。

2PC 的优缺点

● 优点:
保证分布式事务的原子性。
实现简单,易于理解。
● 缺点:
存在单点故障(协调者故障可能导致阻塞)。
性能开销较大(需要多次通信)。

2PC 的 Java 实现

5.1 参与者接口与实现

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

// 参与者接口
interface Participant {
    boolean prepare() throws SQLException;
    void commit() throws SQLException;
    void rollback() throws SQLException;
}

// 具体的数据库参与者
class DatabaseParticipant implements Participant {
    private String url;
    private String user;
    private String password;
    private String sql;
    private Object[] params;

    public DatabaseParticipant(String url, String user, String password, String sql, Object[] params) {
        this.url = url;
        this.user = user;
        this.password = password;
        this.sql = sql;
        this.params = params;
    }

    @Override
    public boolean prepare() throws SQLException {
        try (Connection conn = DriverManager.getConnection(url, user, password)) {
            conn.setAutoCommit(false); // 开启事务
            try (var stmt = conn.prepareStatement(sql)) {
                for (int i = 0; i < params.length; i++) {
                    stmt.setObject(i + 1, params[i]);
                }
                stmt.executeUpdate();
            }
            return true; // 准备成功
        }
    }

    @Override
    public void commit() throws SQLException {
        try (Connection conn = DriverManager.getConnection(url, user, password)) {
            conn.commit(); // 提交事务
            System.out.println("Committed: " + url);
        }
    }

    @Override
    public void rollback() throws SQLException {
        try (Connection conn = DriverManager.getConnection(url, user, password)) {
            conn.rollback(); // 回滚事务
            System.out.println("Rolled back: " + url);
        }
    }
}

5.2 协调者实现

import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

// 协调者
class Coordinator {
    private final List<Participant> participants = new ArrayList<>();
    private final List<Participant> preparedParticipants = new ArrayList<>();

    public void addParticipant(Participant participant) {
        participants.add(participant);
    }

    // 第一阶段:准备阶段
    public boolean prepare() {
        for (Participant p : participants) {
            try {
                if (p.prepare()) {
                    preparedParticipants.add(p); // 记录已成功准备的参与者
                } else {
                    return false; // 如果有参与者准备失败,返回 false
                }
            } catch (SQLException e) {
                e.printStackTrace();
                return false;
            }
        }
        return true; // 所有参与者准备成功
    }

    // 第二阶段:提交或回滚
    public void commit(boolean success) {
        for (Participant p : preparedParticipants) { // 只对已成功准备的参与者操作
            try {
                if (success) {
                    p.commit(); // 提交事务
                } else {
                    p.rollback(); // 回滚事务
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

5.3 测试主程序

public class TwoPhaseCommit {
    public static void main(String[] args) {
        // 初始化参与者
        Participant participant1 = new DatabaseParticipant(
                "jdbc:mysql://localhost:3306/db1", "root", "password",
                "UPDATE accounts SET balance = balance - ? WHERE id = ?",
                new Object[]{100.00, 1}
        );

        Participant participant2 = new DatabaseParticipant(
                "jdbc:mysql://localhost:3306/db2", "root", "password",
                "UPDATE accounts SET balance = balance + ? WHERE id = ?",
                new Object[]{100.00, 1}
        );

        // 初始化协调者
        Coordinator coordinator = new Coordinator();
        coordinator.addParticipant(participant1);
        coordinator.addParticipant(participant2);

        // 第一阶段:准备阶段
        boolean prepared = coordinator.prepare();
        if (prepared) {
            // 第二阶段:提交
            coordinator.commit(true);
        } else {
            // 回滚
            coordinator.commit(false);
        }
    }
}

6. 运行说明

  1. 准备阶段:
    ● 参与者 1 从账户 1 扣除 100。
    ● 参与者 2 向账户 2 增加 100。
  2. 提交阶段:
    ● 如果所有参与者都准备成功,提交事务。
    ● 如果任一参与者准备失败,回滚事务。

7. 总结

2PC 是分布式事务的基础协议,通过准备和提交两个阶段保证事务的原子性。
本示例通过 Java 实现了 2PC 的核心逻辑,适用于分布式数据库场景。
实际应用中,2PC 可以与其他技术(如 TCC、Saga)结合,解决单点故障和性能问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值