分布式事务和2阶段提交

在当今世界,数据正在以惊人的速度增长。单台计算机的存储容量是有限的,因此需要将数据分布在多台机器或数据库上,这种技术被称为分布式技术。

互联网大厂中很多系统都在采用微服务架构。在这种架构中,每个微服务都管理自己的数据库。如果需要在这些数据库中添加或修改数据,就需要调用该数据库所负责的微服务。

将数据分布在多台机器上会带来一系列挑战。对于单体系统来说,关系数据库如Postgres、MySQL等本身就提供ACID(原子性、一致性、隔离性、持久性)属性。

同样的情况并不适用于分片数据库或分布在微服务中的数据。在本文中,我们将了解处理分布式事务时的原子性。我们将会看到一个名为两阶段提交(2-Phase Commit)的协议,它能帮助我们实现这一点。那么,让我们开始吧。

单体系统

让我们先以一个简单的单体银行应用程序为例。应用程序负责处理用户的银行交易,该系统会与一个单一数据库进行增删改查。

当用户A向用户B转账时,我们需要确保以下几点:

  1. 如果交易成功,用户B的账户会增加余额,用户A的账户会减少相应的余额。
  2. 交易完成后,数据库服务器可能会崩溃。然而,它必须能恢复到崩溃前的状态。
  3. 交易可能由于多种原因失败。例如:用户A可能没有足够的余额。在这种情况下,两个用户的账户都不应更新。
  4. 交易完成后,数据库需要处于一致的状态。例如:在用户A扣减之前,用户B不应收到记入的金额。

用户A向用户B转账20美元:

交易后的可能状态:

如果您使用的是关系型数据库,它将保证上述所有四点。关系数据库使用事务来实现这几点。事务是一个抽象概念,它封装了一组操作。它会保证所有操作要么全部成功,要么全部不执行,这就是事物的原子性。

简单来说,事务是一组数据库可以执行的SQL语句。数据库执行每个SQL语句。如果出现故障,它将中止事务。当事务被中止时,底层数据不会发生变化。从状态的角度来看,相当于没有执行任何语句。

如果所有语句都执行,事务将被提交。一旦事务提交,底层数据将被修改并持久化。

对于上述例子,数据库事务将包括以下语句:

Begin
update set balance = balance + 20 where user = ‘B’;
update set balance = balance - 20 where user = ‘A’;
Commit

假设用户A和B的初始余额分别是40美元和60美元。在执行上述事务时,有以下可能性:

  • 成功 - 在这种情况下,事务将被提交。用户A的余额将为20美元,用户B的余额将为80美元。如果数据库在此后崩溃,恢复后也会回到这个状态。
  • 失败 - 如果在更新用户A的余额时发生故障,数据库将中止事务,并回滚所有更改。用户A和B的余额都不会被改变,也就是说用户A余额仍然是40美元,用户B余额仍然是60美元。

数据库分片

我们现在决定扩展我们的数据库,以满足越来越多的客户需求。数据分布在多个数据库服务器(实例)上。因此,用户A和用户B的数据库记录可能在不同的实例中。

在这种情况下,我们还能保证原子性吗?答案是不能,在处理多个数据库服务器时,保证分布式事物的原子性则落在应用程序的开发者身上了。

以上面用户A向B转账20美元为例。我们将不得不在两个独立的MySQL上执行这两个SQL查询。如果其中一个SQL查询失败,将导致状态不一致。我们希望避免这种不一致状态。

我们必须确保要么事务成功完成,要么失败全部不执行。我们不希望将事务中途留在不一致的状态。下面要介绍的两阶段提交(2-Phase Commit)就是用来保证这一点的。

两阶段提交(2-Phase Commit)

我们现在来看一下两阶段协议的工作原理。我们引入一个新的东东,称为事务协调器(Transaction Coordinator)。它的作用是协调事务的提交部分。管理单个事务的其他服务器称为参与者(Participants)。

在我们的例子中,我们有两个事务Txn Credit和Txn Debit。Txn Credit在分片A上运行,Txn Debit在分片B上运行。客户端启动这两个事务并将它们发送到两个分片。下图说明了这个过程。两个数据库服务器开始执行事务。

稍后,客户端向事务协调器发送提交消息。事务提交现在被事务协调器分为两个阶段。

在第一阶段,RequestCommit消息被发送到所有参与服务器。每个服务器必须对该消息做出响应,返回OK或FAIL消息。如果服务器能够成功执行事务,则返回OK消息。如果在执行期间出现任何错误,则返回FAIL消息。例如:如果在借记事务中账户余额变为负数。

事务协调器等待所有服务器的响应。一旦收到响应,它将决定是提交还是中止事务。这成为提交的第二阶段。只有在每个服务器都回复OK消息时,事务才会被提交。如果至少有一个服务器返回FAIL消息,事务将被中止。

下图显示了每个服务器都返回OK消息的情况。每个其他服务器收到协调器的提交消息,事务成功。

在FAIL消息的情况下,事务协调器向所有参与者发送中止消息。结果,参与者回滚各自的事务。

上述过程确保了分布式事务的原子性。事务要么在所有服务器上提交,要么在所有服务器上回滚。但不会中途留在不一致的状态。不存在一个账户被记入而另一个账户未被扣减的情况。

两阶段提交的缺点

我们现在来探讨两阶段提交的缺点。以下是使用两阶段提交在分布式系统中的主要缺点:

  • 延迟:正如我们所见,事务协调器等待所有参与服务器的响应,然后进行提交的第二阶段。这增加了延迟,客户端可能会感受到执行的缓慢。因此,两阶段提交并不适合对性能要求高的应用程序。
  • 单点故障:事务协调器有时会成为单点故障。在事务协调器向所有参与者发送提交消息之前,它可能会宕机。在这种情况下,参与者上运行的所有事务将进入阻塞状态。它们只有在协调器恢复并发送提交信号后才会提交。
  • 参与者依赖性:一个慢速的参与者会影响其他参与者的性能。总事务时间与最慢服务器的时间成正比。如果事务在单个服务器上失败,则必须在所有其他服务器上回滚。这可能会导致资源浪费。

以上所有2PC的缺点都可以通过Saga模式来克服。Saga模式依赖于最终一致性来处理分布式事务。我们将在后面的文章中探讨这种模式。

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

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

NXP i.MX 6系列处理器加入“产品长期供货计划”

近期,NXP(恩智浦半导体)的i.MX 6系列处理器已加入其“产品长期供货计划”,不同型号处理器的生命周期得到了10~15年的延长,确保了长期稳定的供货与维护。 (NXP产品长期供货计划的目的,是给客户的…

pycharm关闭项目时,页面卡住了,怎么办?

问题 在关闭pycharm时,有时会遇到卡在退出进度条的界面,很讨厌,那我们要怎么办才能退出呢? 说明:本篇文章不是从根源上解决这个问题,无法避免这种情况。 解决方法 方法一: 在卡住时&#xf…

Redis 7.x 系列【27】集群原理之通信机制

有道无术,术尚可求,有术无道,止于术。 本系列Redis 版本 7.2.5 源码地址:https://gitee.com/pearl-organization/study-redis-demo 文章目录 1. 概述2 节点和节点2.1 集群拓扑2.2 集群总线协议2.3 流言协议2.4 心跳机制2.5 节点握…

【推研小灶】复旦与南大之间:一次独特的计算机保研之旅

写在前面 上午10点填完志愿等待复试通知,利用这段时间记录一下我简短的夏令营和预推免。今年变为线下之后,部分学校的入营情况、考核方式有明显变化。加上CS方向保研名额总体变多,形势有点小乱,甚至填报系统都在9.29中秋节当天&a…

Python如何获取终端尺寸?

os.get_terminal_size(),无差别获取当前终端长宽,让你为所欲为。 (笔记模板由python脚本于2024年07月27日 08:30:53创建,本篇笔记适合喜欢钻研的coder翻阅) 【学习的细节是欢悦的历程】 Python 官网:https://www.python.org/ Fre…

【Python系列】Parquet 数据处理与合并:高效数据操作实践

💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

unity2D游戏开发08脚本化对象

创建Scriptable Object 在scripts文件夹下创建一个名为Sriptable Objects的文件夹,然后在文件夹里面创建一个名为Item的脚本 using System.Collections; using System.Collections.Generic; using UnityEngine;//[CreateAssetMenu] 是一个属性(Attribute),用于告诉Unity编…

非UI设计师勿入,英文B端界面的确有可取之处.

作为中文的UI设计师,可以从英文B端界面中汲取设计灵感的几种方法: 观察布局和结构: 注意英文B端界面的布局和结构,包括导航栏、侧边栏、内容区域等。观察它们的排列方式、比例和空白间隙的运用,可以借鉴到自己的设计中…

GeoHash原理介绍以及在redis中的应用

GeoHash将二维信息编码成了一个一维信息。降维后有三个好处: 编码后数据长度变短,利于节省存储。利于使用前缀检索当分割的足够细致,能够快速的对双方距离进行快速查询 GeoHash是一种地址编码方法。他能够把二维的空间经纬度数据编码成一个字符串。 1…

十一、【Python】基础教程-【Python全掌握】六大基础数据类型:布尔类型的终极指南

目录 一、基础类型“布尔型”处理方法 1. 直接赋值和使用 2. 布尔值的逻辑运算 3. 条件语句中的布尔值 4. 布尔值转换 5. 短路逻辑 6. 在循环和迭代中的使用 一、基础类型“布尔型”处理方法 在Python中,布尔类型是一种基本的数据类型,用于表示逻…

3DMAX一键藤球建模插件RattanBall使用方法

3DMAX一键藤球建模插件RattanBall使用教程 3DMAX藤球建模插件RattanBall,一键创建藤球模型,可以设置藤球大小、嵌套层数等,简单实用,一键生成! 【适用版本】 3dMax2018.2及更高版本 【安装方法】 3DMAX一键藤球建模插…

Animate软件基础:创建及插入关键帧

这里讲一下Animate软件中创建或插入关键帧的基本方法。 FlashASer:Animate教程及作品源文件https://zhuanlan.zhihu.com/p/677437436 FlashASer:实用的各种Adobe Animate软件教程https://zhuanlan.zhihu.com/p/675680471 FlashASer:Animat…

UE5.4内容示例(1)- 学习笔记

https://www.unrealengine.com/marketplace/zh-CN/product/content-examples 《内容示例》是学习UE5的基础示例,可以用此示例熟悉一遍UE5的功能 模型与材质部分 StaticMeshes FBX_Import_Options Material_Advanced Material_Decals Material_Instances Material_N…

SpringBoot教程(十七) | SpringBoot集成swagger

SpringBoot教程(十七) | SpringBoot集成swagger 一、Swagger的简述二、SpringBoot集成swagger21. 引入依赖2. 新建SwaggerConfig配置类当 SpringBoot为2.6.x及以上时 需要注意 3.配置Swagger开关4. 给Controller 添加注解(正式使用&#xff0…

Radxa ROCK 5B+开发板基本配置和上手测试

目录 1.ROCK 5B Plus开发板是什么?2.烧录官方系统3.设置ROOT用户4.开发板温度情况5.VNC远程桌面配置6.WIFI模块测速7.M2接口使用注意8.总结 1.ROCK 5B Plus开发板是什么? ROCK 5B(即ROCK 5B Plus,本文用ROCK 5B指代) …

AMQP-核心概念-4

本文参考以下链接摘录翻译: https://www.rabbitmq.com/tutorials/amqp-concepts 绑定 (Bindings) 绑定是交换机用来将消息路由到队列的规则。为了让一个交换机E将消息路由到队列Q,Q必须绑定到E。绑定可以有一个可选属性routing key,有一些类…

VTX326蓝牙TTS语音合成芯片赋能电子称重一体机人机交互新革新

引言 随着科技的飞速发展,零售业正经历着前所未有的变革。北京宇音天下科技有限公司,作为行业的领跑者,推出了革命性的VTX326蓝牙TTS语音合成芯片,为超市、水果店、熟食店、麻辣烫店等零售业态带来了智能化的全新体验。 市场与趋…

【C语言】文件操作详解!!!

目录 为什么要使用文件? 文件概念 1. 什么是文件? 2. 程序文件 3. 数据文件 4. 文件名 文件的使用 1. 文件指针 2. 文件的打开与关闭 文件的顺序读写 1. 顺序读写函数 2. scanf系列与printf系列 文件的随机读写 1. fseek 2. ftell 3. …

数据结构第二讲:顺序表

数据结构第二讲:顺序表 1.线性表2.什么是顺序表3. 静态顺序表4.动态顺序表4.1顺序表基础4.2顺序表的初始化4.3顺序表的销毁4.4顺序表的尾插4.5顺序表的头插4.6顺序表的尾删4.7顺序表的头删4.8顺序表在指定位置之前插入数据4.9顺序表删除指定位置的数据4.10顺序表查找…