强一致性的皇冠:分布式事务模型的至高法则揭秘

关注微信公众号 “程序员小胖” 每日技术干货,第一时间送达!

引言

分布式事务模型是分布式系统设计的核心,关键在于保证数据一致性和事务完整性,尤其强调强一致性。诸如2PC、3PC、Saga、TCC等模型与协议,应运而生以解决分布式ACID实现难题,包括处理网络延迟、节点故障及并发控制等。它们追求在一致性和系统性能、可用性上的平衡。在微服务和云计算时代,高效灵活的分布式事务管理对于构建可靠、高性能系统尤为关键,对技术人员来说,精通这些模型及其应用场景,对提升系统质量和用户体验至关重要。

2PC

两阶段提交(2PC)故名思义,是有两个步骤组成的.其中涉及到两个角色 TM(事务管理器)和 RM(资源管理器)

准备阶段(Phase 1)

事务协调者向所有参与者发送事务预提交请求,询问是否准备好提交事务。

TM(事务管理器)通知各个RM(资源管理器)准备提交它们的事务分支。如果RM判断自己进行的工作可以被提交,那就对工作内容进行持久化,再给TM肯定答复;要是发生了其他情况,那给TM的都是否定答复。

提交阶段(Phase 2)

如果协调者收到所有参与者的“准备成功”响应,它会向所有参与者发送“提交事务”命令;

TM根据阶段1各个RM prepare的结果,决定是提交还是回滚事务。如果所有的RM都prepare成功,那么TM通知所有的RM进行提交;如果有RM prepare失败的话,则TM通知所有RM回滚自己的事务分支。

以mysql数据库为例,在第一阶段,事务管理器向所有涉及到的数据库服务器发出prepare"准备提交"请求,数据库收到请求后执行数据修改和日志记录等处理,处理完成后只是把事务的状态改成"可以提交",然后把结果返回给事务管理器。

如果第一阶段中所有数据库都prepare成功,那么事务管理器向数据库服务器
发出"确认提交"请求,数据库服务器把事务的"可以提交"状态改为"提交完成"状态,然后返回应答。如果在第一阶段内有任何一个数据库的操作发生了错误,或者事务管理器收不到某个数据库的回应,则认为事务失败,回撤所有数据库的事务。数据库服务器收不到第二阶段的确认提交请求,也会把"可以提交"的事务回撤。

使用场景

金融交易:如银行间的资金转账,确保资金不会凭空产生或消失。

库存管理系统:在电子商务中,确保商品的库存数量准确无误。

import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

// 简化版的参与者
interface Participant {
    boolean prepare();
    void commit();
    void rollback();
}

// 简化的协调者
class Coordinator {
    private List<Participant> participants;
    private ExecutorService executor;

    public Coordinator(List<Participant> participants) {
        this.participants = participants;
        this.executor = Executors.newFixedThreadPool(participants.size());
    }

    public void startTransaction() {
        try {
            List<Future<Boolean>> prepareResults = preparePhase();
            if (allTrue(prepareResults)) {
                commitPhase();
            } else {
                rollbackPhase();
            }
        } finally {
            executor.shutdown();
        }
    }

    private List<Future<Boolean>> preparePhase() {
        return executor.invokeAll(participants, preparingTask());
    }

    private void commitPhase() {
        participants.forEach(Participant::commit);
    }

    private void rollbackPhase() {
        participants.forEach(Participant::rollback);
    }

    private boolean allTrue(List<Future<Boolean>> results) throws InterruptedException {
        for (Future<Boolean> result : results) {
            if (!result.get()) return false;
        }
        return true;
    }

    private Runnable preparingTask() {
        return () -> {
            // 模拟参与者的行为,实际中参与者会在此执行本地事务并返回结果
            return true; // 返回true表示准备成功,false表示失败
        };
    }
}

// 示例参与者
class MockParticipant implements Participant {
    @Override
    public boolean prepare() {
        // 实现本地事务的预提交检查逻辑
        return true; // 假设总是成功
    }

    @Override
    public void commit() {
        // 执行事务的提交操作
        System.out.println("Committing transaction...");
    }

    @Override
    public void rollback() {
        // 执行事务的回滚操作
        System.out.println("Rolling back transaction...");
    }
}

public class TwoPhaseCommitDemo {
    public static void main(String[] args) {
        List<Participant> participants = List.of(new MockParticipant(), new MockParticipant());
        Coordinator coordinator = new Coordinator(participants);
        coordinator.startTransaction();
    }
}

2PC存在的问题

二阶段提交看起来确实能够提供原子性的操作,但是不幸的是,二阶段提交还是有几个缺点的:

  • 同步阻塞问题

2PC 中的参与者是阻塞的。在第一阶段收到请求后就会预先锁定资源,一直到 commit 后才会释放。

  • 单点故障

由于协调者的重要性,一旦协调者TM发生故障,参与者RM会一直阻塞下去。尤其在第二阶段,协调者发生故障,那么所有的参与者还都处于锁定事务资源的状态中,而无法继续完成事务操作。

  • 数据不一致

若协调者第二阶段发送提交请求时崩溃,可能部分参与者收到commit请求提交了事务,而另一部
分参与者未收到commit请求而放弃事务,从而造成数据不一致的问题。

3PC

三阶段提交(3PC, Three-Phase Commit)模型是对两阶段提交(2PC)的一个改进,旨在减少阻塞范围并提高系统的可用性,同时仍然追求强一致性。3PC通过引入预提交阶段来减少参与者在不确定状态下的时间,从而降低了事务长时间阻塞的风险。

CanCommit阶段

3PC的CanCommit阶段其实和2PC的准备阶段很像。协调者向参与者发送commit请求,参与者如果可以提交就返
回Yes响应,否则返回No响应。

  1. 事务询问 协调者向参与者发送CanCommit请求。询问是否可以执行事务提交操作。然后开始等待参与者的响
    应。
  2. 响应反馈 参与者接到CanCommit请求之后,正常情况下,如果其自身认为可以顺利执行事务,则返回Yes响应,并进入预备状态。否则反馈No

PreCommit阶段

协调者根据参与者的反应情况来决定是否可以记性事务的PreCommit操作。根据响应情况,有以下两种可能。
假如协调者从所有的参与者获得的反馈都是Yes响应,那么就会执行事务的预执行。

  1. 发送预提交请求 协调者向参与者发送PreCommit请求,并进入Prepared阶段。
  2. 事务预提交 参与者接收到PreCommit请求后,会执行事务操作,并将undo和redo信息记录到事务日志中。
  3. 响应反馈 如果参与者成功的执行了事务操作,则返回ACK响应,同时开始等待最终指令。

假如有任何一个参与者向协调者发送了No响应,或者等待超时之后,协调者都没有接到参与者的响应,那么就执行事务的中断。

  1. 发送中断请求 协调者向所有参与者发送abort请求。
  2. 中断事务 参与者收到来自协调者的abort请求之后(或超时之后,仍未收到协调者的请求),执行事务的中断。

doCommit阶段

该阶段进行真正的事务提交,也可以分为以下两种情况

场景1:执行提交

  1. 发送提交请求 协调接收到参与者发送的ACK响应,那么他将从预提交状态进入到提交状态。并向所有参与者发送doCommit请求。
  2. 事务提交 参与者接收到doCommit请求之后,执行正式的事务提交。并在完成事务提交之后释放所有事务资源。
  3. 响应反馈 事务提交完之后,向协调者发送Ack响应。
  4. 完成事务 协调者接收到所有参与者的ack响应之后,完成事务。

场景 2:中断事务

协调者没有接收到参与者发送的ACK响应(可能是接受者发送的不是ACK响应,也可能响
应超时),那么就会执行中断事务。

  1. 发送中断请求 协调者向所有参与者发送abort请求
  2. 事务回滚 参与者接收到abort请求之后,利用其在阶段二记录的undo信息来执行事务的回滚操作,并在完成
    回滚之后释放所有的事务资源。
  3. 反馈结果 参与者完成事务回滚之后,向协调者发送ACK消息
  4. 中断事务 协调者接收到参与者反馈的ACK消息之后,执行事务的中断。

看到这里其实我们能看出来3PC是2PC的升级版本。三阶段提交相较于两阶段提交主要有两个改动点:

  1. 引入超时机制。同时在协调者和参与者中都引入超时机制。
    2.在第一阶段和第二阶段中插入一个准备阶段。保证了在最后提交阶段之前各参与节点的状态是一致的。也就是说,除了引入超时机制之外,3PC把2PC的准备阶段再次一分为二,这样三阶段提交就有CanCommit、PreCommit、DoCommit三个阶段。

使用场景

  • 金融交易:确保资金在多个账户间转移过程中的精确和不可逆。
  • 分布式数据库同步:在多副本数据库系统中,确保数据更改在所有节点上一致。
  • 关键业务流程:如保险索赔处理,需要高度一致性的步骤执行。
class Coordinator {
    List<Participant> participants;
    
    void start3PCTransaction() {
        // 第一阶段:Prepare
        if (allParticipantsReadyToPrepare()) {
            // 第二阶段:PreCommit
            if (allParticipantsReadyToPreCommit()) {
                // 第三阶段:Commit
                commitTransaction();
            } else {
                abortTransaction();
            }
        } else {
            abortTransaction();
        }
    }
    
    private boolean allParticipantsReadyToPrepare() {
        // 实现逻辑:询问所有参与者是否准备好
        return true; // 简化处理
    }
    
    private boolean allParticipantsReadyToPreCommit() {
        // 实现逻辑:询问所有参与者是否准备好预提交
        return true; // 简化处理
    }
    
    private void commitTransaction() {
        // 实现逻辑:通知所有参与者提交事务
    }
    
    private void abortTransaction() {
        // 实现逻辑:通知所有参与者事务失败,需要回滚
    }
}

interface Participant {
    boolean canPrepare();
    boolean canPreCommit();
    void commit();
    void rollback();
}

// 示例参与者
class MockParticipant implements Participant {
    @Override
    public boolean canPrepare() { return true; }
    @Override
    public boolean canPreCommit() { return true; }
    @Override
    public void commit() { System.out.println("Committing..."); }
    @Override
    public void rollback() { System.out.println("Rolling Back..."); }
}

结语

总结而言,分布式事务模型在构建强一致性分布式系统中扮演着不可或缺的角色,是确保数据一致性和事务完整性的基石。面对分布式环境的固有挑战,诸如两阶段提交(2PC)、三阶段提交(3PC),为开发者提供了实现强一致性的多样化手段。这些模型通过精心设计的协议,在数据一致性、系统可用性与性能之间寻找微妙的平衡点。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Unity初级---初识生命周期

1. Awake() &#xff1a;唤醒函数&#xff0c;最先执行的函数&#xff0c;只执行一次&#xff0c;当脚本文件挂载的对象被激活时调用 2. OnEnable() &#xff0c;OnDisable()&#xff1a;当脚本启用和禁用时触发&#xff0c;可执行多次&#xff0c;触发的前提是脚本挂载的对象…

QT开发(四) 制作一个JSON检查小工具

1、JSON概念 1.1 定义 JSON&#xff08;JavaScript Object Notation&#xff09;是一种轻量级的数据交换格式&#xff0c;它易于人类阅读和编写&#xff0c;同时也易于机器解析和生成。JSON基于JavaScript语言的子集&#xff0c;但是独立于编程语言&#xff0c;因此可以被多种…

C语言----汉诺塔问题

1.什么是汉诺塔问题 简单来说&#xff0c;就是有三个柱子&#xff0c;分别为A柱&#xff0c;B柱&#xff0c;C柱。其中A柱从上往下存放着从小到大的圆盘&#xff0c;我们需要借助B柱和C柱&#xff0c;将A柱上的所有圆盘转移到C柱上&#xff0c;并且一次只能移动一个圆盘&#…

【Qt QML】Qt Quick Scene Graph

Qt Quick 2是一个用于创建图形界面的库&#xff0c;它使用一个专门的场景图&#xff08;Scene Graph&#xff09;来进行渲染。通过使用OpenGL ES、OpenGL、Vulkan、Metal或Direct 3D等图形API&#xff0c;Qt Quick 2可以有效地优化图形渲染过程。使用场景图而不是传统的命令式绘…

1688工厂货源API接口:用于商品采集、商品搜索、商品详情数据抓取

item_get 获得1688商品详情item_search 按关键字搜索商品item_search_img 按图搜索1688商品&#xff08;拍立淘&#xff09;item_search_suggest 获得搜索词推荐item_fee 获得商品快递费用seller_info 获得店铺详情item_search_shop 获得店铺的所有商品item_password 获得淘口令…

在拥有多个同名称密码的ap环境中,如何连接到指定信道或mac的ap路由器?

在给客户做ESP32-C3入墙开关项目时&#xff0c;客户问&#xff1a;在拥有多个同名称密码的ap环境中&#xff0c;如何连接到指定信道或mac的ap路由器&#xff1f;针对这个问题&#xff0c;启明云端工程师给出下面解决方法。 1、将wifi_sta_config_t配置中的channel配置为该信道…

神经网络怎么把隐含层变量融合到损失函数中?

&#x1f3c6;本文收录于「Bug调优」专栏&#xff0c;主要记录项目实战过程中的Bug之前因后果及提供真实有效的解决方案&#xff0c;希望能够助你一臂之力&#xff0c;帮你早日登顶实现财富自由&#x1f680;&#xff1b;同时&#xff0c;欢迎大家关注&&收藏&&…

matplotlib和pandas与numpy

1.matplotlib介绍 一个2D绘图库&#xff1b; 2.Pandas介绍&#xff1a; Pandas一个分析结构化数据的工具&#xff1b; 3.NumPy 一个处理n纬数组的包&#xff1b; 4.实践&#xff1a;绘图matplotlip figure()生成一个图像实例 %matplotlib inline&#xff1a;图形直接在…

前端传递list(数组)类型参数,后端接收失败

一顿报错,我之前遇到的list都是Long类型 貌似用GET也是可以的,但是很奇怪一直报错 就是不可以 后来去百度 查询到可以用两种方法解决这个问题 1、拆开 传 以GET方式&#xff0c;后端GetMappingRequestParam接收。 2、以Post方式传&#xff0c;后端创建dto PostMappingReques…

elementUI table表格相同元素合并行----支持多列

效果图如下: vue2代码如下&#xff1a; 只粘贴了js方法哦&#xff0c; methods: {// 设置合并行 setrowspans() { const columns [‘name’, ‘value’]; // 需要合并的列名 // 为每个需要合并的列设置默认 rowspan this.tableData.forEach(row > { columns.forEach(col …

光电探测器性能指标测试

光电探测器的三个核心指标&#xff1a; 带宽&#xff0c;转换增益&#xff0c;噪声(信噪比&#xff0c;NEP&#xff0c;噪声密度) 测试环境&#xff1a;可调谐激光器&#xff08;CW LASER&#xff09;&#xff0c;强度调制器(AM)&#xff0c;信号发生器(AWG)&#xff0c;可调衰…

【算法】滑动窗口——最大连续1的个数

本篇文章讲的是“最大连续1的个数”这道题&#xff0c;从最开始的简单暴力到用滑动窗口算法实现解题的思路历程&#xff0c;有需要借鉴即可。 目录 1.题目2.暴力求解3.滑动窗口解法3.1优化一&#xff1a;end重返start优化&#xff0c;end指针不回退3.2优化二&#xff1a;某一st…

Day_2

1. 菜品管理 新增菜品 接口设计 1. 根据类型查询分类&#xff08;分类管理已完成&#xff09; 查看接口文档即可 2. 文件上传 创建Bucket 采用的是阿里云的OSS对象存储服务 新增AccessKey 3. 菜品的新增逻辑 代码开发 1. 文件上传接口开发 为了提高代码的解耦性&#…

Java_方法引用

方法引用就是把已经有的方法拿过来用&#xff0c;当作函数式接口中抽象方法的方法体。 条件&#xff1a; 1.引用处需要是函数式接口 2.被引用的方法需要已经存在 3.被引用的方法的形参和返回值需要跟抽象方法的形参和返回值保持一致 4.被引用方法的功能需要满足当前的要求 简…

ATA-2161高压放大器用途有哪些种类

高压放大器是一种电子设备&#xff0c;其主要功能是将输入信号放大到较高的电压水平&#xff0c;同时保持信号的形状和特性。这种设备在各种应用领域中都有重要作用&#xff0c;它的种类繁多&#xff0c;根据不同的用途可以分为多种类型。 1.医学领域 在医学设备中&#xff0c;…

搭建Harbor仓库

文章目录 Harbor仓库搭建Harbor仓库安装 docker 服务修改配置文件 Harbor仓库 搭建Harbor仓库 下载 Harbor 仓库 安装 docker 服务 # step 1: 安装必要的一些系统工具 yum install -y yum-utils device-mapper-persistent-data lvm2 # Step 2: 添加软件源信息 yum-config-m…

notepad++安装 hex-editor插件

打开notepad 点击插件 搜索 hex-editor,点击右侧 安装install 安装成功后&#xff0c;在已安装插件中就有显示了

Java性能优化(五)-多线程调优-Lock同步锁的优化

作者主页&#xff1a; &#x1f517;进朱者赤的博客 精选专栏&#xff1a;&#x1f517;经典算法 作者简介&#xff1a;阿里非典型程序员一枚 &#xff0c;记录在大厂的打怪升级之路。 一起学习Java、大数据、数据结构算法&#xff08;公众号同名&#xff09; ❤️觉得文章还…

《QT实用小工具·五十九》随机图形验证码,带有一些可人的交互与动画

1、概述 源码放在文章末尾 该项目实现了可交互的动画验证码控件&#xff0c;趣味性十足&#xff1a; 字符变换动画 噪音动画 可拖动交互 项目demo演示如下所示&#xff1a; 项目部分代码如下所示&#xff1a; #ifndef CAPTCHAMOVABLELABEL_H #define CAPTCHAMOVABLELABEL…

【影片欣赏】【指环王】【魔戒:护戒使者 The Lord of the Rings: The Fellowship of the Ring】

2001年发行&#xff0c;Extended DVD Edition Part One 1. Prologue: One Ring to Rule Them All… 2. Concerning Hobbits 3. The Shire 4. Very Old Friends 5. A Long-expected Party 6. Farewell Dear Bilbo 7. Keep It Secret, Keep It Safe 8. The Account of Isildur 9…