分布式事务之2PC两阶段提交

news/2025/2/1 14:09:30/文章来源:https://www.cnblogs.com/dafanjoy/p/18696257

1. 分布式事务概述

1.1 问题背景

在分布式系统中,业务操作可能跨越多个服务或数据库(如订单服务、库存服务、支付服务),传统单机事务(ACID)无法满足跨网络节点的数据一致性需求。

  • 网络不可靠:服务间调用可能失败或超时。
  • 数据一致性:不同节点间的状态需最终一致。
  • 性能与可用性:避免长时间锁资源导致系统阻塞。

分布式事务的核心目标是确保 跨服务/数据库的操作要么全部成功,要么全部回滚

2. 分布式事务解决方案

2.1 两阶段提交(2PC)

原理

  • 阶段一(Prepare):协调者询问所有参与者是否可提交,参与者锁定资源并返回“同意”或“拒绝”。
  • 阶段二(Commit/Rollback):若所有参与者同意,协调者发送提交命令;否则发送回滚命令。

以下是一个简化的 Java 两阶段提交(2PC) 具体实现示例,包含协调者(Coordinator)和参与者(Participant)的核心逻辑。代码通过模拟数据库操作展示2PC的关键流程:


1. 参与者(Participant)实现

每个参与者代表一个独立的数据库或服务,需支持准备(Prepare)、提交(Commit)、回滚(Rollback)操作。

import java.util.concurrent.atomic.AtomicBoolean;/*** 参与者(如数据库或服务)*/
public class Participant {private String name;          // 参与者名称(如"DB1")private AtomicBoolean prepared = new AtomicBoolean(false);  // 准备状态private AtomicBoolean committed = new AtomicBoolean(false); // 提交状态public Participant(String name) {this.name = name;}/*** 阶段一:准备操作(锁定资源)* @return true表示准备成功,false表示失败*/public boolean prepare() {try {// 模拟资源锁定,实际可能为操作数据库System.out.println(name + ": Trying to prepare...");Thread.sleep(100);  // 模拟网络延迟boolean success = Math.random() > 0.2;  // 80%概率成功if (success) {prepared.set(true);System.out.println(name + ": Prepared successfully.");return true;} else {System.out.println(name + ": Prepare failed.");return false;}} catch (InterruptedException e) {Thread.currentThread().interrupt();return false;}}/*** 阶段二:提交操作*/public void commit() {if (prepared.get()) {// 实际提交事务(如更新数据库)committed.set(true);System.out.println(name + ": Committed.");} else {System.out.println(name + ": Cannot commit without preparation.");}}/*** 阶段二:回滚操作*/public void rollback() {if (prepared.get()) {// 实际回滚事务(如恢复数据)prepared.set(false);System.out.println(name + ": Rolled back.");} else {System.out.println(name + ": No need to rollback.");}}// 检查是否已提交public boolean isCommitted() {return committed.get();}
}

2. 协调者(Coordinator)实现

协调者负责管理所有参与者,驱动两阶段提交流程。

import java.util.List;/*** 协调者(事务管理器)*/
public class Coordinator {private List<Participant> participants;public Coordinator(List<Participant> participants) {this.participants = participants;}/*** 执行两阶段提交事务* @return true表示事务成功提交,false表示失败*/public boolean executeTransaction() {System.out.println("===== Phase 1: Prepare =====");boolean allPrepared = participants.stream().allMatch(Participant::prepare);System.out.println("===== Phase 2: Commit/Rollback =====");if (allPrepared) {participants.forEach(Participant::commit);System.out.println("Transaction committed successfully.");return true;} else {participants.forEach(Participant::rollback);System.out.println("Transaction rolled back due to failures.");return false;}}
}

3. 客户端测试代码

模拟包含两个参与者的分布式事务场景。

import java.util.Arrays;public class TwoPhaseCommitDemo {public static void main(String[] args) {// 创建两个参与者(如数据库DB1和DB2)Participant db1 = new Participant("DB1");Participant db2 = new Participant("DB2");// 创建协调者并关联参与者Coordinator coordinator = new Coordinator(Arrays.asList(db1, db2));// 执行两阶段提交事务boolean success = coordinator.executeTransaction();// 输出最终状态System.out.println("\nFinal Status:");System.out.println("DB1 Committed: " + db1.isCommitted());System.out.println("DB2 Committed: " + db2.isCommitted());System.out.println("Transaction Result: " + (success ? "SUCCESS" : "FAILURE"));}
}

4. 运行结果示例

成功场景(所有参与者准备成功)

===== Phase 1: Prepare =====
DB1: Trying to prepare...
DB1: Prepared successfully.
DB2: Trying to prepare...
DB2: Prepared successfully.
===== Phase 2: Commit/Rollback =====
DB1: Committed.
DB2: Committed.
Transaction committed successfully.Final Status:
DB1 Committed: true
DB2 Committed: true
Transaction Result: SUCCESS

失败场景(某一参与者准备失败)

===== Phase 1: Prepare =====
DB1: Trying to prepare...
DB1: Prepared successfully.
DB2: Trying to prepare...
DB2: Prepare failed.
===== Phase 2: Commit/Rollback =====
DB1: Rolled back.
DB2: No need to rollback.
Transaction rolled back due to failures.Final Status:
DB1 Committed: false
DB2 Committed: false
Transaction Result: FAILURE

5. 关键点说明

  1. 阶段一(Prepare)

    • 协调者询问所有参与者是否可以提交。
    • 参与者锁定资源并记录操作日志。
    • 任一参与者失败则整个事务回滚。
  2. 阶段二(Commit/Rollback)

    • 若所有参与者准备成功,协调者发送提交命令。
    • 若任一参与者失败,协调者发送回滚命令。
  3. 代码简化说明

    • 实际应用中需处理网络超时、重试和持久化日志。
    • 分布式场景下需使用RPC或HTTP替代本地方法调用。
    • 生产环境建议使用成熟的XA协议实现(如Atomikos、Narayana)。

6. 2PC的局限性

  • 同步阻塞:参与者在Prepare阶段后需阻塞等待协调者指令。
  • 单点故障:协调者宕机可能导致事务悬挂。
  • 数据不一致:协调者与参与者在Commit阶段同时宕机时,可能部分提交。

关注微信公众号,查看更多技术文章。

image

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/877568.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【SQL】存储过程、函数、触发器

存储过程 存储过程(Stored Procedure)是一种在数据库中保存的SQL语句集合,它可以执行一系列的数据库操作,例如插入、更新、查询等。存储过程可以提高数据库操作的效率,减少网络流量,并且可以封装复杂的逻辑。定义: 存储过程是一组为了完成特定功能的SQL语句集,这些语句…

[Tools] GitHub Action 部署文档网站

关于部署网站,理论上来讲,只要你有一个服务器,你要采用什么样的方式来部署都是可以的。但是前提是你需要有一个服务器(物理机、云服务器)。 这节课我们部署文档网站选择使用 github 来进行部署,因为 GitHub 为我们提供了一个免费的服务器,一个账号只有一个,只要你在 Gi…

[Tools] VitePress搭建文档网站

创建 API 文档可以选择如下的 3 种方式:功能较少,可以直接写在 README.md 文件里面 内容较多,可以单独写一个文件 API 数量众多(Vue、React 这种级别),需要考虑单独拿一个网站来做详细的文档支持这里我们要搭建的网站实际上就是一个文档网站,这个时候我们可以选择静态站…

[Tools] 搭建文档网站

创建 API 文档可以选择如下的 3 种方式:功能较少,可以直接写在 README.md 文件里面 内容较多,可以单独写一个文件 API 数量众多(Vue、React 这种级别),需要考虑单独拿一个网站来做详细的文档支持这里我们要搭建的网站实际上就是一个文档网站,这个时候我们可以选择静态站…

java中的HsahMap

HsahMap HashMap 是 Java 中最常用的集合类之一,它实现了 Map 接口,基于哈希表存储键值对 HashMap的存储是无顺序的 HashMap存储的是键值对(key-value)其中键key是唯一的,值(value)可以重复。 HashMap的底层是数组和链表 HashMap的常见方法 添加方法: 1,put(K key, V valu…

MyBatis的增删改查实现

首先还是使用上一篇文章相同的数据表和实体类,如下图所示1 package com.loubin.pojo;2 3 public class User {4 private int id;5 private String name;6 private String pwd;7 8 public int getId() {9 return id; 10 } 11 12 public void …

Reqable:现代化 API 调试工具

Reqable 是一款专为开发者设计的现代化 API 调试工具,旨在简化 API 开发、测试和调试的流程。 它支持多种协议(如 HTTP、HTTPS、WebSocket 等),并提供了丰富的功能,帮助开发者更高效地构建、调试和分析 API 请求与响应。 Reqable 以其简洁的设计、强大的功能和卓越的性能,…

DeepSeek-R1?别信新闻,它真能打败 OpenAI?

Yy DeepSeek-R1?别信新闻 它真的是那个打败 OpenAI 的开源黑马模型,还是又一条假新闻? Fabio Matricardi它真的那么厉害……是在哪方面? 前言:DeepSeek在大模型小型以及优化的道路上探索蒸馏和纯强化学习的路径获得的一点成绩确实让美国人紧张了一把,但同时也招致了更严格…

6.演讲比赛流程管理系统

基于STL的演讲比赛流程管理系统。1.需求2.文件目录3.代码 3.1Speaker.h #pragma once #include<iostream> using namespace std;class Speaker {public:string m_Name;double m_Score[2];};3.2SpeechManager.h #pragma once #include<iostream> #include<vector…

Qwen2.5-VL:更强大的多模态大模型|附实测结果

模型更新简述 几天前,通义千问更新了最新的多模态大模型Qwen2.5-VL,拥有包含 3B、7B 和 72B 在内的 3 个模型尺寸,同时完全开源,可在huggingface和modelscope下载到所有模型权重。 1. 更灵活的时空维度处理 ● 空间维度上,动态地将不同尺寸的图像转换为不同长度的token,并…