当前位置: 首页 > article >正文

分布式事务解决方案全解析:从经典模式到现代实践

前言

在分布式系统中,数据一致性是一个核心问题。随着微服务架构的普及,跨服务、跨数据库的操作变得越来越普遍,如何保证这些操作的原子性、一致性、隔离性和持久性(ACID)成为了一个极具挑战性的任务。本文将全面介绍 2PC、TCC、最大努力通知、本地消息表、Saga、XA 协议、可靠消息传输、Seata AT 模式 等主流分布式事务解决方案,并通过 Java 示例代码和 流程图帮助您更直观地理解每种方案。


一、2PC(两阶段提交)

简介

两阶段提交(2PC)是一种经典的分布式事务协议,分为准备阶段和提交阶段两个步骤。它通过协调者(Coordinator)来控制多个参与者的事务执行。

工作原理

  1. 准备阶段:协调者询问所有参与者是否可以提交事务。
  2. 提交阶段:如果所有参与者都同意,则协调者发送提交命令;否则回滚事务。

UML图

协调者 参与者A 参与者B 准备请求 准备请求 准备就绪 准备就绪 所有参与者准备就绪 提交请求 提交请求 已提交 已提交 回滚请求 回滚请求 已回滚 已回滚 alt [提交] [中止] 协调者 参与者A 参与者B

Java 示例代码

interface Participant {boolean prepare();void commit();void rollback();
}class ParticipantA implements Participant {@Overridepublic boolean prepare() {System.out.println("参与者A:准备完成");return true;}@Overridepublic void commit() {System.out.println("参与者A:提交完成");}@Overridepublic void rollback() {System.out.println("参与者A:回滚完成");}
}class TwoPhaseCommitCoordinator {private List<Participant> participants;public TwoPhaseCommitCoordinator(List<Participant> participants) {this.participants = participants;}public void executeTransaction() {boolean allPrepared = true;for (Participant participant : participants) {if (!participant.prepare()) {allPrepared = false;break;}}if (allPrepared) {for (Participant participant : participants) {participant.commit();}} else {for (Participant participant : participants) {participant.rollback();}}}
}

二、TCC(Try-Confirm-Cancel)

简介

TCC 是一种补偿型的分布式事务解决方案,将业务逻辑分为三个阶段:

  1. 尝试(Try):预留资源。
  2. 确认(Confirm):真正执行业务操作。
  3. 取消(Cancel):撤销预留资源。

UML图

服务A 服务B 尝试操作 尝试结果 判断是否继续 确认操作 确认结果 取消操作 取消结果 alt [成功] [失败] 服务A 服务B

Java 示例代码

interface TccTransaction {boolean tryOperation();void confirm();void cancel();
}class ServiceA implements TccTransaction {@Overridepublic boolean tryOperation() {System.out.println("服务A:尝试操作");return true;}@Overridepublic void confirm() {System.out.println("服务A:确认操作");}@Overridepublic void cancel() {System.out.println("服务A:取消操作");}
}class TccTransactionManager {private List<TccTransaction> services;public TccTransactionManager(List<TccTransaction> services) {this.services = services;}public void executeTransaction() {boolean allTrySuccess = true;for (TccTransaction service : services) {if (!service.tryOperation()) {allTrySuccess = false;break;}}if (allTrySuccess) {for (TccTransaction service : services) {service.confirm();}} else {for (TccTransaction service : services) {service.cancel();}}}
}

三、最大努力通知

简介

最大努力通知是一种基于最终一致性的分布式事务解决方案,适用于对实时性要求不高但可靠性要求较高的场景。

工作原理

  1. 发送通知:支付服务向订单服务发送支付成功的通知。
  2. 重试机制:如果通知失败,则记录失败日志并通过定时任务重试,直到成功或达到最大重试次数。

UML图

支付服务 订单服务 定时任务 发送支付成功通知 返回成功 记录失败日志 定时重试通知 重复重试,直到成功或达到最大次数 alt [通知成功] [通知失败] 支付服务 订单服务 定时任务

Java 示例代码

public class PaymentService {private OrderService orderService;public PaymentService(OrderService orderService) {this.orderService = orderService;}public void notifyPaymentSuccess(String orderId) {try {boolean success = orderService.notifyPayment(orderId);if (!success) {saveToRetryTable(orderId);}} catch (Exception e) {saveToRetryTable(orderId);}}private void saveToRetryTable(String orderId) {System.out.println("保存到重试表:orderId=" + orderId);}
}public class RetryTask {private PaymentService paymentService;public RetryTask(PaymentService paymentService) {this.paymentService = paymentService;}public void executeRetryTask() {List<String> retryOrders = fetchFromRetryTable();for (String orderId : retryOrders) {paymentService.notifyPaymentSuccess(orderId);}}private List<String> fetchFromRetryTable() {return List.of("order1", "order2");}
}

四、本地消息表

简介

本地消息表通过在每个服务中维护一个消息表来记录需要发送的消息,并通过异步方式将消息发送出去。

工作原理

  1. 写入本地消息表:业务操作完成后,将消息插入消息表。
  2. 异步发送消息:通过后台任务发送消息。

UML图

业务服务 消息表 消息队列 写入消息 异步发送消息 业务服务 消息表 消息队列

Java 示例代码

public class LocalMessageService {public void sendMessage(String message) {// 执行业务操作businessOperation();// 插入本地消息表insertIntoLocalMessageTable(message);// 异步发送消息asyncSendMessage();}private void businessOperation() {System.out.println("执行业务操作");}private void insertIntoLocalMessageTable(String message) {System.out.println("插入本地消息表:" + message);}private void asyncSendMessage() {System.out.println("异步发送消息");}
}

五、Saga

简介

Saga 是一种基于事件驱动的长事务管理方法,使用正向操作和补偿操作来实现最终一致性。

工作原理

  1. 正向操作:依次执行事务中的每个步骤。
  2. 补偿操作:如果某个步骤失败,则依次回滚前面的步骤。

UML图

服务A 服务B 正向操作 正向结果 判断是否继续 下一步操作 补偿操作 alt [成功] [失败] 服务A 服务B

Java 示例代码

interface SagaStep {void execute();void compensate();
}class StepA implements SagaStep {@Overridepublic void execute() {System.out.println("执行步骤A");}@Overridepublic void compensate() {System.out.println("补偿步骤A");}
}class StepB implements SagaStep {@Overridepublic void execute() {System.out.println("执行步骤B");}@Overridepublic void compensate() {System.out.println("补偿步骤B");}
}class SagaManager {private List<SagaStep> steps;public SagaManager(List<SagaStep> steps) {this.steps = steps;}public void executeSaga() {int stepIndex = 0;try {for (SagaStep step : steps) {step.execute();stepIndex++;}} catch (Exception e) {for (int i = stepIndex - 1; i >= 0; i--) {steps.get(i).compensate();}}}
}

六、XA 协议

简介

XA 协议是 X/Open 组织提出的分布式事务处理标准,定义了事务管理器(TM)和资源管理器(RM)之间的接口。XA 协议的核心思想是通过两阶段提交来实现分布式事务。

UML图

事务管理器 资源管理器1 资源管理器2 开始事务 开始事务 准备完成 准备完成 提交事务 提交事务 回滚事务 回滚事务 alt [提交] [回滚] 事务管理器 资源管理器1 资源管理器2

Java 示例代码

import javax.transaction.xa.XAResource;
import javax.transaction.xa.Xid;public class XADemo {public static void main(String[] args) throws Exception {XAResource resource1 = new MyXAResource();XAResource resource2 = new MyXAResource();Xid xid = new MyXid(1, new byte[]{0x01}, new byte[]{0x02});// 开始事务resource1.start(xid, XAResource.TMNOFLAGS);resource2.start(xid, XAResource.TMNOFLAGS);// 执行操作resource1.end(xid, XAResource.TMSUCCESS);resource2.end(xid, XAResource.TMSUCCESS);// 准备提交int result1 = resource1.prepare(xid);int result2 = resource2.prepare(xid);if (result1 == XAResource.XA_OK && result2 == XAResource.XA_OK) {resource1.commit(xid, false);resource2.commit(xid, false);} else {resource1.rollback(xid);resource2.rollback(xid);}}
}

七、可靠消息传输

简介

可靠消息传输依赖于消息队列(如 Kafka、RabbitMQ)来保证消息的可靠传递。通常结合本地事务和消息队列来实现最终一致性。

UML图

生产者 消息队列 消费者 发送消息 推送消息 确认消费 生产者 消息队列 消费者

Java 示例代码

public class ReliableMessageProducer {public void sendMessage(String message) {// 执行业务操作businessOperation();// 发送消息到消息队列sendToMessageQueue(message);}private void businessOperation() {System.out.println("执行业务操作");}private void sendToMessageQueue(String message) {System.out.println("发送消息到消息队列:" + message);}
}public class ReliableMessageConsumer {public void consumeMessage(String message) {// 消费消息并执行业务逻辑processMessage(message);// 确认消费acknowledgeMessage();}private void processMessage(String message) {System.out.println("处理消息:" + message);}private void acknowledgeMessage() {System.out.println("确认消费");}
}

八、Seata AT 模式

简介

Seata 是一种开源的分布式事务解决方案,支持多种模式来解决分布式事务问题,其中 AT(Auto Transaction)模式是一种无侵入式的解决方案,适用于微服务架构下的分布式事务管理。

Seata AT 模式的工作流程
  1. 一阶段提交:业务数据和回滚日志记录在同一个本地事务中提交到数据库。
  2. 二阶段提交:如果所有分支事务都成功,则全局事务提交;如果有任何一个分支失败,则根据回滚日志进行补偿操作。

UML图

应用程序 资源管理器 事务协调器 开始本地事务 返回资源ID 注册分支事务 返回分支ID 提交本地事务 上报事务状态 全局提交 根据回滚日志补偿 alt [所有分支成功] [存在失败分支] 应用程序 资源管理器 事务协调器

Java 示例代码

import io.seata.rm.datasource.DataSourceProxy;
import javax.sql.DataSource;public class SeataATDemo {private DataSource dataSource;public SeataATDemo(DataSource originalDataSource) {// 使用 Seata 的 DataSourceProxy 包装原始的数据源this.dataSource = new DataSourceProxy(originalDataSource);}public void executeBusinessLogic() throws Exception {// 假设这里执行一些涉及多个服务或数据库的操作String sql = "UPDATE account SET balance = balance - 100 WHERE user_id = 'user1';";try (java.sql.Connection connection = dataSource.getConnection();java.sql.PreparedStatement statement = connection.prepareStatement(sql)) {statement.executeUpdate();}}
}

总结

分布式事务的解决方案多种多样,每种方法都有其适用场景:

  • 2PC 和 XA 适合强一致性场景,但性能开销较大。
  • TCC 和 Saga 提供了更高的灵活性,但需要手动设计补偿逻辑。
  • 最大努力通知 和 本地消息表 更适合最终一致性场景,实现简单且高效。
  • 可靠消息传输 依赖消息队列实现高可靠性和最终一致性。
  • Seata AT 模式 提供了一种无侵入式的解决方案,特别适用于微服务架构,简化了分布式事务的处理。

希望本文能为您提供全面的理解,并帮助您在实际项目中选择合适的分布式事务解决方案!

相关文章:

分布式事务解决方案全解析:从经典模式到现代实践

前言 在分布式系统中&#xff0c;数据一致性是一个核心问题。随着微服务架构的普及&#xff0c;跨服务、跨数据库的操作变得越来越普遍&#xff0c;如何保证这些操作的原子性、一致性、隔离性和持久性&#xff08;ACID&#xff09;成为了一个极具挑战性的任务。本文将全面介绍…...

软件工程面试题(二十七)

1、j a v a 对象初始化顺序 1.类的初始化(initialization class & interface) 2.对象的创建(creation of new class instances) 顺序:应为类的加载肯定是第一步的,所以类的初始化在前。大体的初始化顺序是: 类初始化 -> 子类构造函数 -> 父类构造函数 -&g…...

fastGPT—nextjs—mongoose—团队管理之部门相关api接口实现

创建部门或者子部门 import type { NextApiRequest, NextApiResponse } from next; import { NextAPI } from /service/middleware/entry; import { MongoOrgModel } from fastgpt/service/support/permission/org/orgSchema;async function handler(req: NextApiRequest, res…...

C++ 数据竞态检查

-fsanitizethread 编译时&#xff0c;添加参数-fsanitizethread -g&#xff0c;可以运行态检查数据竞态问题&#xff0c;包括&#xff1a; 数据竞态死锁锁、条件变量错误使用 check_tsan 开源库 yalantinglibs有段检查编译器是否支持 fsanitize 编译参数的宏&#xff0c;挺…...

逛好公园的好处

逛公园和软件开发看似是两个不同的活动&#xff0c;但它们之间存在一些有趣的关联和相互促进的关系&#xff1a; 激发创造力&#xff1a;公园中的自然景观、多样的人群以及各种活动能为开发者带来新的灵感和创意。软件开发过程中&#xff0c;从公园中获得的创意可以帮助开发者设…...

C++开发工具全景指南

专业编译与调试工具深度解析 2025年4月 编译器套件 GNU Compiler Collection (GCC) GNU编译器套件是自由软件基金会开发的跨平台编译器系统&#xff0c;支持C、C、Objective-C、Fortran、Ada等多种编程语言。作为Linux系统的标准编译器&#xff0c;GCC以其强大的优化能力和…...

【网络安全】 防火墙技术

防火墙是网络安全防御的重要组成部分&#xff0c;它的主要任务是阻止或限制不安全的网络通信。在这篇文章中&#xff0c;我们将详细介绍防火墙的工作原理&#xff0c;类型以及如何配置和使用防火墙。我们将尽可能使用简单的语言和实例&#xff0c;以便于初学者理解。 一、什么…...

文档的预解析

1. 预解析的核心目标 浏览器在正式解析&#xff08;Parsing&#xff09;HTML 前&#xff0c;会启动一个轻量级的 预解析器&#xff08;Pre-Parser&#xff09;&#xff0c;快速扫描文档内容&#xff0c;实现&#xff1a; 提前发现并加载关键资源&#xff08;如 CSS、JavaScrip…...

理解“功能内聚”

链接&#xff1a; 理解“偶然内聚” 理解“逻辑内聚” 理解“时间内聚” 理解“过程内聚” 理解“通信内聚” 理解“顺序内聚” 理解“功能内聚” 功能内聚&#xff08;Functional Cohesion&#xff09;是最高级别的内聚形式&#xff0c;指的是模块内的所有元素都紧密地围绕着一…...

windows 常用命令总结

工作中用到的 Linux 总结&#xff08;持续更新中...&#xff09;_linux工作经验-CSDN博客 PS&#xff1a; 推荐使用 powershell 而不是 cmd&#xff0c;因为PowerShell 是一个更先进和功能更强大的工具&#xff08; powershell 有命令记忆功能&#xff0c;比较方便&#xff09…...

记一次表格数据排序优化(一)--排序30000条数据有多卡

目录 需求 第一次尝试 运行环境 思路 存储 排序 触发排序操作 如何实现高效的排序 关键1 关键2 关键3 磨刀不误砍柴工 关键4 代码 效果 卡顿原因分析 原因1 原因2 第二次尝试 需求 1 我的qt程序通过表格显示30000条数据。数据来自udp&#xff0c;udp每隔10秒…...

图形渲染中的定点数和浮点数

三种API的NDC区别 NDC全称&#xff0c;Normalized Device Coordinates Metal、Vulkan、OpenGL的区别如下&#xff1a; featureOpenGL NDCMetal NDCVulkan NDC坐标系右手左手右手z值范围[-1,1][0,1][0,1]xy视口范围[-1,1][-1,1][-1,1] GPU渲染的定点数和浮点数 定点数类型&a…...

【深度学习】CNN简述

文章目录 一、卷积神经网络&#xff08;CNN&#xff09;二、CNN结构特性1. CNN 典型结构2. 局部连接3. 权重共享4.空间或时间上的次采样 三、理解层面 一、卷积神经网络&#xff08;CNN&#xff09; 卷积神经网络(Convolutional Neural Network&#xff0c;CNN)是一种用于处理…...

强化学习课程:stanford_cs234 学习笔记(3)introduction to RL

文章目录 前言7 markov 实践7.1 markov 过程再叙7.2 markov 奖励过程 MRP&#xff08;markov reward process&#xff09;7.3 markov 价值函数与贝尔曼方程7.4 markov 决策过程MDP&#xff08;markov decision process&#xff09;的 状态价值函数7.4.1 状态价值函数7.4.2 状态…...

紫檀博物馆一游与软件开发

今天去逛了中国紫檀博物馆&#xff0c;里边很多层展品&#xff0c;也有一些清代的古物&#xff0c;檀木&#xff0c;黄花梨木家具和各种摆件&#xff0c;馆主陈丽华女士也是发心复原、保留和弘扬中国的传统文化&#xff0c;和西游记唐僧扮演者迟成瑞先生一家。 每一件展品都精…...

RocketMQ初认识

ProducerCustomerNameServer: Broker的注册服务发现中心BrokerServer:主要负责消息的存储、投递和查询以及服务高可用保证 RocketMQ的集群部署&#xff1a; 单个master的分支多个Master 模式&#xff1a;集群中有多个 Master 节点&#xff0c;彼此之间相互独立。生产者可以将消…...

第十三章:持久化存储_《凤凰架构:构建可靠的大型分布式系统》

第十三章 持久化存储 一、Kubernetes存储设计核心概念 &#xff08;1&#xff09;存储抽象模型 PersistentVolume (PV)&#xff1a;集群级别的存储资源抽象&#xff08;如NFS卷/云存储盘&#xff09;PersistentVolumeClaim (PVC)&#xff1a;用户对存储资源的声明请求&#…...

Chrome开发者工具实战:调试三剑客

在前端开发的世界里&#xff0c;Chrome开发者工具就是我们的瑞士军刀&#xff0c;它集成了各种强大的功能&#xff0c;帮助我们快速定位和解决代码中的问题。今天&#xff0c;就让我们一起来看看如何使用Chrome开发者工具中的“调试三剑客”&#xff1a;断点调试、调用栈跟踪和…...

教程:如何使用 JSON 合并脚本

目录 1. 介绍 2. 使用方法 3. 注意事项 4. 示例 5.完整代码 1. 介绍 该脚本用于将多个 COCO 格式的 JSON 标注文件合并为一个 JSON 文件。COCO 格式常用于目标检测和图像分割任务&#xff0c;包含以下三个主要部分&#xff1a; "images"&#xff1a;图像信息&a…...

C++/Qt 模拟sensornetwork的工作

C/Qt 可视化模拟sensornetwork的工作 C/Qt 模拟sensornetwork的工作 C/Qt 可视化模拟sensornetwork的工作内容简介&#xff08;一&#xff09; 需求和规格说明&#xff08;1&#xff09;问题描述&#xff08;2&#xff09;设计目的&#xff08;3&#xff09;基本要求&#xff0…...

ffmpeg音频分析

对一个16k 单声道音频&#xff0c;生成频谱图 ./ffmpeg -i input.wav -lavfi "showspectrumpics800x400:modecombined:scalelin:gain1.5" spectrum.png...

【多线程】CAS机制

目录 一. CAS的概念 二. CAS的原理 三.标准库中的CAS 四. CAS的应用 &#xff08;1&#xff09;原子类的使用 &#xff08;2&#xff09; 自旋锁的实现 五. CAS的ABA问题 一. CAS的概念 CAS&#xff08;Compare And Swap&#xff09;机制是一种无锁的并发控制技术&#…...

音视频(四)android编译

前言 前面已经讲了在windows上应用了&#xff0c;这章主要讲述android上编译 1&#xff1a;环境 git 如果失败 直接跑到相应网站 手动下载 ubuntu22.* android ndk r21e download:https://developer.android.google.cn/ndk/downloads/index.html?hluk 为什么用这个&#xff0…...

Chapter07_图像压缩编码

文章目录 图像压缩编码图像压缩编码基础图像压缩的基本概念信息相关信息冗余信源编码及其分类 图像编码模型信源编码器模型信源解码器模型 数字图像的信息熵信源符号码字平均长度信息熵信息量 变长编码费诺码霍夫曼编码 位平面编码格雷码 图像压缩编码 数字图像的压缩是指在满…...

团体设计程序天梯赛L2-025 # 分而治之

文章目录 题目解读输入格式输出格式 思路Ac Code参考 题目解读 在战争中&#xff0c;我们希望首先攻下敌方的部分城市&#xff0c;使其剩余的城市变成孤立无援&#xff0c;然后再分头各个击破。为此参谋部提供了若干打击方案。本题就请你编写程序&#xff0c;判断每个方案的可…...

Linux网络套接字

Socket 编程 UDP 本章将函数介绍和代码编写实战一起使用。 IPv4 的 socket 网络编程,sockaddr_in 中的成员 struct in_addr.sin_addr 表示 32 位 的 IP 地址 但是我们通常用点分十进制的字符串表示 IP 地址,以下函数可以在字符串表示和in_addr 表示之间转换; 字符串转 in_addr…...

看爬山虎学本领 软爬机器人来创新 各种场景能适应

*本文只做阅读笔记分享* 一、灵感来源&#xff1a;向植物取经 大家好&#xff01;今天来聊一款超酷的软爬机器人&#xff0c;它的灵感来自会攀爬的植物——爬山虎。 大家都知道&#xff0c;爬墙高手爬山虎能在各种复杂墙面轻松生长攀爬&#xff0c;可现有的攀爬机器人在复杂…...

1-Docker安装

1.准备环境 1.第一步&#xff1a;创建以自己的姓名全拼的用户名 [roothadoop ~]# useradd qiwenyong [roothadoop ~]# passwd qiwenyong Changing password for user qiwenyong. New password: BAD PASSWORD: The password is shorter than 7 characters Retype new passwor…...

WPS JS宏编程教程(从基础到进阶)-- 第三部分:JS宏编程语言开发基础

第三部分:JS宏编程语言开发基础 @[TOC](第三部分:JS宏编程语言开发基础)**第三部分:JS宏编程语言开发基础**1. 变量与数据类型**变量声明:三种方式****示例代码****数据类型判断****实战:动态处理单元格类型**2. 运算符全解析**算术运算符****易错点:字符串拼接 vs 数值相…...

BT-Basic函数之首字母T

BT-Basic函数之首字母T 文章目录 BT-Basic函数之首字母Ttabtesttest conttest monitortest on boardstest scanworkstest shortstesthead cleanuptesthead configurationtesthead istesthead power on/offtesthead statustestjet print level istestordertestplan generationth…...