Dubbo生态之初识分布式事务

news2024/11/18 6:03:39

1.分布式事务简介

传统的关系型数据库只能保证单个数据库中多个数据表的事务特性。一旦多个SQL操作涉及到多个数据库,这类的事务就无法解决跨库事务问题。在传统架构下,这种问题出现的情况非常少,但是在分布式微服务架构中,分布式事务的问题变得更加突出。

举例,假设我们要涉嫌下面电商系统中的支付功能。

 分析: 上图中有3个服务 支付服务、资金服务和红包服务

当用户发起支付时,就会涉及到几个事务操作

  • 创建支付订单
  • 从资金服务中扣除余额
  • 从红包服务中扣除余额
  • 更新支付结果
这是四个典型的事务操作,而且这些操作分别属于不同的数据库,最终期望 的结果是希望这三个服务所对应的数据是一致的,很显然传统的事务无法解 决这个问题!

 于是,分布式事务就诞生了。说到分布式事务,我们就不得不提以下X/OpenDTP事务模型

2.X/OpenDTP事务模型

这个事务模型定义了一套分布式事务的标准,也就是定义了规范和API接口,并且这个标准提出了二阶段提交(2PC-Two-Phase-Commit)来保证分布式事务的完整性。

2.1 二阶段提交模型

那么什么是二阶段提交2PC协议呢?

如上图所示, 在分布式事务中,多个小事务的提交与回滚,只有当前进程知道,其它进程是不清楚的。而为了实现多个数据库的事务一致性,就必然需要引入第三方节点来进行事务协调,如下图所示,

通过一个全局的分布式事务协调,从而来实现多个数据库事务的提交与回滚,在这样的架构下,事务的管理方式就变成了两个步骤。

a.开启事务并向各个数据库节点写入事务日志

b.根据第一个步骤中各个节点的执行结果,来决定对事务进行提交或回滚。

 这就是所谓的2PC提交协议,用图来表示为如下:

 

 2PC的提交流程如下:

1. 表决阶段:此时 TM(协调者)向所有的参与者发送一个 事务请求,参与者在收到这请求后,如果准备好了(写事务日志)就会向 TM发送一个 执行成功 消息作为回应,告知 TM 自己已经做好了准备,否则会返回一个 失败 消息;
2. 提交阶段:TM 收到所有参与者的表决信息,如果所有参与者一致认为可以提交事务,那么 TM就会发送 提交 消息,否则发送 回滚 消息;对于参与者而言,如果收到 提交 消息,就会提交本地事务,否则就会取消本地事务。 

2.2 X/OpenDTP事务模型 

基于对上述2PC的了解,我们再来看下X/OpenDTP事务模型,如下图所示

 如上图所示,X/OpenDTP模型定义了三个角色和两个协议,其中三个角色为

AP(Application Program),表示应用程序,也可以理解成使用DTP模型的程序
RM(Resource Manager),资源管理器,这个资源可以是数据库, 应 用程序通过资源管理器对资源进行控制,资源管理器必须实现XA定义的接口
TM(Transaction Manager),表示事务管理器,负责协调和管理全局事务,事务管理器控制整个全局事务,管理事务的生命周期,并且协调资源。

两个协议分别是

 XA协议: XA 是X/Open DTP定义的资源管理器和事务管理器之间的接口 规范,TM用它来通知和协调相关RM事务的开始、结束、提交或回滚。目前Oracle、Mysql、DB2都提供了对XA的支持; XA接口是双向的系统接口,在事务管理器(TM)以及多个资源管理器之间形成通信的桥梁(XA不 能自动提交)

XA {START|BEGIN} xid [JOIN|RESUME] --负责开启或者恢复一个事务分支,并且管理XID到调用线程
XA END xid [SUSPEND [FOR MIGRATE]] --负责取消当前线程与事务分支的关联
XA PREPARE xid --负责询问RM 是否准备好了提交事务分支
XA COMMIT xid [ONE PHASE] --知RM提交事务分支
XA ROLLBACK xid --通知RM回滚事务分支
XA RECOVER [CONVERT XID]

 TX协议: 全局事务管理器与资源管理器之间通信的接口

在分布式系统中,每一个机器节点虽然都能够明确知道自己在进行事务操作过程中的结果是成功还是失败,但却无法直接获取到其他分布式节点的操作结果。因此当一个事务操作需要跨越多个分布式节点的时候,为了保持事务处理的ACID特性,就需要引入一个“协调者”(TM)来统一调度所有分布式节点的执行逻辑,这些被调度的分布式节点被称为AP。TM负责调度AP的行为,并最终决定这些AP是否要把事务真正进行提交到(RM)

 2.3 基于XA协议的2PC提交流程

二阶段提交,是计算机网络尤其是在数据库领域内,为了使基于分布式系统架构下的所有节点在进行事务处理过程中能够保持原子性和一致性而设计的一种算法。通常,二阶段提交协议也被认为是一种一致性协议,用来保证分布式系统数据的一致性。目前,绝大部分的关系型数据库都是采用二阶段提交协议来完成分布式事务处理的,利用该协议能够非常方便地完成所有分布式事务AP的协调,统一决定事务的提交或回滚,从而能够有效保证分布式数
据一致性,因此2pc也被广泛运用在许多分布式系统中。

 

整体流程如上图所示,下面为演示Mysql使用XA实现分布式事务提交:

-- 启动一个XA事务 (xid 必须是一个唯一值; [JOIN|RESUME] 字句不被支持)
xa start 'xatest1';
insert into ums_user(username,password) values('test1','test1');
-- 结束一个XA事务 ( [SUSPEND [FOR MIGRATE]] 字句不被支持)
xa end 'xatest1';
-- 准备 此动作会把这个事务的redo日志写入innodb redo log,只要这一阶段是成功的,那么后续XACommit一定会成功
xa prepare 'xatest1';
XA COMMIT 'xatest1'; //提交事务
-- 或者回滚代码,如果xa prepare这个环节出现错误,事务协调者就把这个事务回滚。
xa rollback 'xatest'; //回滚

 但是在这个过程中,数据库需要提供针对分布式事务处理的接口给到事务管理器,这样事务管理器就能够基于这些接口来协调各个资源的事务提交和回滚操作。也就是说,数据库层面必须要支持。

2.4 基于XA协议的开源框架

主流的数据库如Oracle、mysql都支持XA协议,因此都可以基于xa协议规范,通过二阶段提交来实现数据的一致性。

J2EE就遵循了这些规范,设计还并实现了Java里面的分布式事务编程接口规范-JTA,我们可以利用这些API来完成各个数据库的事务一致性处理。

但是,在XA事务中,根据前面我们的操作过程可以发现,另外,在XACOMMIT阶段,如果其中一个RM因为网络超时没有收到数据提交的指令,会导致数据不一致。
所以如果我们要基于这些API来实现一个比较成熟的分布式事务解决方案, 还需要考虑到这些问题并提出解决方案。比如针对这个问题,我们可以采用 重试的机制来完成数据一致性。

 因此,针对这类分布式事务解决方案的开源框架也很多,比如

Atomikos,Atomikos是为Java平台提供的开源的事务管理工具,它包含收费和开源两个版本,开源版本基本能满足我们的需求。
Bitronix,是一个流行的开源JTA事务管理器实现,你可以使用spring-boot-starter-jta-bitronixstarter为项目添加合适的Birtronix依赖。
Seata事务,阿里巴巴开源的事务解决方案

3.分布式事务解决方案

对于我们的分布式事务来说,存在着网络通信的不确定性,比如当分布式事务中的其中一个节点故障,导致无法提交的情况下,我们究竟是抛弃呢? 还是等待?如果是抛弃,那么我们的数据强一致性就无法保证,但是如果是等待,又容易造成大量线程阻塞,系统性能严重受损。换句话说,我们是要保证可用性呢还是一致性,二者只能选其一。

3.1 CAP理论

CAP理论说的就是分布式架构下的数据一致性和性能问题的平衡方案,

C:Consistency 一致性 同一数据的多个副本是否实时相同。
A:Availability 可用性 可用性:一定时间内 & 系统返回一个明确的结果则称为该系统可用。
P:Partition tolerance 分区容错性 将同一服务分布在多个系统中,从而保证某一个系统宕机,仍然有其他系统提供相同的服务。

CAP理论告诉我们,在分布式系统中,C,A,P三个条件中我们最多只能选择两个,那么问题就来了,我们究竟选择那两个条件较为合适呢?

 对于一个业务系统来说,可用性和分区容错性是必须要满足的两个条件,并且这两者是相辅相成的。业务系统之所以使用分布式系统,主要原因有两个 :

提升整体性能 当业务量猛增,单个服务器已经无法满足我们的业务需求的时候,就需要使用分布式系统,使用多个节点提供相同的功能,从而整体上提升系统的性能,这就是使用分布式系统的第一个原因。
实现分区容错性 单一节点 或 多个节点处于相同的网络环境下,那么会存在一定的风险,万一该机房断电、该地区发生自然灾害,那么业务系统就全面瘫痪了。为了防止这一问题,采用分布式系统,将多个子系统分布在不同的地域、不同的机房中,从而保证系统高可用性。

 这说明了分区容错性是分布式系统的根本,如果分区容错性不能满足,那么使用分布式系统将失去意义。

此外,可用性对业务系统也尤为重要。在大谈用户体验的今天,如果业务系统时常出现'系统异常',响应时间过长等情况,这就使得用户对系统的好感度大打折扣。

在互联网行业竞争激烈的今天,相同领域的竞争者不甚枚举,系统的间歇性 不可用会立马导致用户流向竞争对手。因此,我们只能通过牺牲一致性来换取系统的 可用性 分区容错

 因此,例如Zookeeper中就采用了基于少数服从多数的2pc落地方案

因此,也引出了另外一个理论,叫Base理论

3.2 Base理论

CAP理论告诉我们一个悲惨但不得不接受的实时--我们只能在C,A,P中选择两个条件。而对于业务系统而言,我们往往选择牺牲一致性来换取系统的可用性和分区容错性。不过这里要指出的是,所谓的“牺牲一致性”并不是完全放弃数据一致性,而是牺牲强一致性换取弱一致性。

BA:Basic Available 基本可用
        整个系统在某些不可抗力的情况下,仍然能够保证“可用性”,即一定 时间内仍然能够返回一个明确的结果。只不过“基本可用”和“高可用” 的区别是:
                1.“一定时间”可以适当延长 当举行大促时,响应时间可以适当延长
                2.给部分用户返回一个降级页面 给部分用户直接返回一个降级页面,从而缓解服务器压力。但要注意,返回降级页面仍然是返回明确结果。
S:Soft State:柔性状态 同一数据的不同副本的状态,可以不需要实时 一致。
E:Eventual Consisstency:最终一致性 同一数据的不同副本的状态,可以不需要实时一致,但一定要保证经过一定时间后仍然是一致的。

所以,对于服务来说,也有很多方案可供选择

  • 提供查询服务确认数据状态 
  • 幂等操作对于重发保证数据的安全性
  • TCC事务操作
  • 补偿操作
  • 定期校对

4. 基于可靠性消息的最终一致性方案

最终一致性方案,也称为弱一致性方案

它是基于BASE理论的落地。假设我们要实现一个用户服务里面去实现用户购买,积分服务里面增加用户的积分

@Transactional
public void register(){
    // 用户交易 // local transaction(失败)
    // 发送MQ // mq 成功了 --> 意外获得了积分
}

这样可能存在两种异常情况  用户交易成功,积分失败了 ; 用户交易失败,积分成功了

那么我们怎么要才能保证分布式事务的解决,以及数据的一致性呢。一方面我们要保证事务参与方接收消息的可靠性;另一方面还有就是消息重复消费的问题,消息重复发送怎么保证

4.1 事务参与方接收消息的可靠性

事务参与方接收消息的可靠性可以采用本地事务的方式来解决, 新增一个消息表,来记录MQ的消息发送,发送成功,那么事务同时进行,如果新增用户失败,则回滚,如果消息发送失败了,那就定时任务接着发,但同时也会记录消息表,比如采用一个状态记录成功或失败,定时任务后面可以接着发送

整体交互流程如下:

1. 用户注册 :用户服务在本地事务新增用户和增加 “积分消息日志”。(用 户表和消息表通过本地事务保证一致)下边是伪代码,这种情况下,本 地数据库操作与存储积分消息日志处于同一个事务中,本地数据库操作 与记录消息日志操作具备原子性。
begin transaction;
//1.新增用户
//2.存储积分消息日志
commit transation;

 2. 定时任务扫描日志,在第一步中,我们把需要发送到消息队列的事务消 息保存到了消息日志表,为了保证消息能够百分之百的发送给消息队 列,这里可以启动一个定时任务不断扫描这个消息表中的消息发送到消 息队列。当消息队列反馈发送成功后,删除该消息日志,否则等到下一 个任务周期重试

 3. 消息的可靠性消费,主流的MQ都带了消息确认机制(ack),消费者监 听MQ的消息。

        1. 当消费者受到消息并处理完成后,返回一个ACK给到MQ,告诉MQ 该消息已经消费完成,MQ不需要再向消费者投递该消息。
        2. 否则,MQ会不断重新投递这个消息给到消费者。 当消费者收到“新增积分”消息后,根据该消息的逻辑规则完成指定用户 的积分更新,再基于MQ的ACK机制,从而可以实现可靠的消息投递功能

4.2 消息重复投递的幂等性保障

所谓幂等性,就是MQ重复调用多次产生的业务结果与调用一次产生的业务结果相同。

在分布式架构中,我们调用一个远程服务去完成一个操作,除了成功和失败 以外,还有未知状态,那么针对这个未知状态,我们会采取一些重试的行为;
或者在消息中间件的使用场景中,消费者可能会重复收到消息。对于这两种情况,消费端或者服务端需要采取一定的手段,也就是考虑到重发的情况下保证数据的安全性。一般我们常用的手段:
  • 消息表  用MD5+唯一约束
  • redis SetNx(md5,5min)
  • 状态机  数据的状态(当前状态在状态机中只会存在一次)
  • 上游生成唯一id

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

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

相关文章

java方法负载问题

先介绍一下方法的重载 下面是例子 方法名都为sum而形参是不同的 记住! 是否为重载关系 1在同一个类里面 2形参不同(与返回值无关) 3方法名一样 第一个图为什么错? 答案:虽然在同一个类里面,并且方法名…

3-Django项目继续--初识ModelForm

目录 ModelForm 认识ModelForm 优势 初识Form 初识ModelForm 添加信息 views.py add_student_new.html 修改信息 views.py views.py add_student_new.html ModelForm 认识ModelForm 优势 1、方便校验用户提交的数据 2、页面展示错误提示 3、数据库字段很多的情况…

基于Docker的ROS开发

本文主要介绍如何使用Docker在Windows和Linux环境中部署并使用ROS,通过Docker Container运行ROS,可以方便我们在一个本地环境中运行多个ROS版本。 更多内容,访问专栏目录获取实时更新。 关于ROS的版本 参考ROS1 Distribution Wiki和ROS2 Dis…

SpringBoot项目启动时提示程序包不存在和找不到符号

一、前言 最近接手同事开发的一个Springboot工作项目,从svn上整体拉取下来后,构建完成后,启动的时候遇到了程序包找不到的情况,记录一下处理过程; 二、项目问题 1、报错信息:启动后报 java: 程序包org.sp…

最强端侧多模态模型MiniCPM-V 2.5,8B 参数,性能超越 GPT-4V 和 Gemini Pro

前言 近年来,人工智能领域掀起了一股大模型热潮,然而大模型的巨大参数量级和高昂的算力需求,限制了其在端侧设备上的应用。为了打破这一局限,面壁智能推出了 MiniCPM 模型家族,致力于打造高性能、低参数量的端侧模型。…

【Qt秘籍】[004]-Qt中的重要工具-介绍

QtCreator概览 当我们打开系统的菜单翻到刚刚下载的Qt文件,里面的内容却让我们眼花缭乱。 不过别急,下面我们将一一解析。 1.Assistant Qt自带的离线版本官方文档 2.Designer Qt图形化设计界面的工具,通过拖拽控件快速生成界面&#xff0c…

APM 2.8外置罗盘校准

请注意: GPS不可以飞控带电插拔,带电插拔会产生差分电压,可能会导致GPS模块损坏,无法搜星。不听劝告,后果自负! 1.如何接线 GPS有两根线,要插上面图所示的两个接口。同时拔掉旁边的跳线帽&…

强化学习——学习笔记2

在上一篇文章中对强化学习进行了基本的概述,在此篇文章中将继续深入强化学习的相关知识。 一、什么是DP、MC、TD? 动态规划法(DP):动态规划法离不开一个关键词,拆分 ,就是把求解的问题分解成若…

[AIGC] Nginx常用变量详解

Nginx非常强大,其主要功能包括HTTP服务器、反向代理、负载均衡等。Nginx的配置中有许多内置的变量,你可以在配置文件中使用这些变量进行灵活的配置。在本篇文章中,我们将介绍一些Nginx中常见的变量,包括proxy_add_header。 常见变…

多旋翼无人机机场考哪些内容?

多旋翼无人机机场考试的内容主要包括理论和实飞两部分。 理论考试主要涵盖无人机相关的知识,包括无人机的原理、结构、操作规范等。 实飞考试则主要考察飞行技能,包括飞行操作、航线规划、飞行稳定性等。 具体来说,实飞部分可能包括使用GPS…

前端渲染页面的原理

之前一直不愿意写一篇关于原理的,因为说起来实在是太繁杂,要写得细,码字梳理,计算下来起码都要差不多三周。以前一直躲避这个事情,现在反正有时间,为了不荒废自己,那就从头捋一遍。也方便自己后…

【STL库源码剖析】list 简单实现

从此音尘各悄然 春山如黛草如烟 目录 list 的结点设计 list 的迭代器 list 的部分框架 迭代器的实现 容量相关相关函数 实现 insert 在指定位置插入 val 实现 push_back 在尾部进行插入 实现 erase 在指定位置删除 实现 pop_back 在尾部进行删除 实现 list 的头插、头删 实现…

循环buffer“一写多读“

1.往期回顾 一个简单实用的循环buffer,用于缓冲数据!测试500M数据,耗时1.3秒。 C语言版本的循环buffer比C版本的速度更快!测试500M数据0.5秒,达9.25Gbps左右! C 语言免拷贝版本循环 buffer 比拷贝版本快了…

熵值法(熵权法)

熵值法(Entropy Method)是一种多属性决策分析方法,主要用于权重确定、排序和评价。它在风险评估、资源配置、环境管理等领域得到广泛应用。熵值法的核心思想是基于信息熵的概念,利用信息熵来度量各属性对决策的贡献程度&#xff0…

农产品产品防伪防窜货+二维码防伪+溯源系统源码全平台一物一码数字化防伪防窜货和溯源查询系统

农产品产品防伪防防窜货二维码防伪溯源系统源码全平台一物一码数字化防伪防窜货和溯源查询系统 产品防伪防防窜货二维码防伪溯源系统源码,该系统采用最简单易用的phpMySQL进行搭建,拥有完善的网站前后台,通过对每件产品生产线上的单品、二级…

BIGO前端CICD平台

本文首发于:https://github.com/bigo-frontend/blog/ 欢迎关注、转载。 我是谁 BIGO前端CICD平台,是一个服务于前端团队的全研发周期管理平台,已经是我们团队日常都要使用的工具了。 该平台实现了一键创建项目、发布编排、新建迭代、checkl…

【微服务】部署mysql集群,主从复制,读写分离

两台服务器做如下操作 1.安装mysqldocker pull mysql:5.72.启动以及数据挂载 mkdir /root/mysql/data /root/mysql/log /root/mysql/conf touch my.conf //mysql的配置文件docker run --name mysql \ -e MYSQL_ROOT_PASSWORD123456 \ -v /root/mysql/data:/var/lib/mysql \ -v…

如何创建一个vue项目?详细教程,如何创建第一个vue项目?

已经安装node.js在自己找的到的地方新建一个文件夹用于存放项目,记住文件夹的存放路径,以我为例,我的文件夹路径为D:\tydic 打开cmd命令窗口,进入刚刚的新建文件夹 切换硬盘: D: 进入文件夹:cd tydic 使…

基于ViutualBox+Ubuntu(Linux)的开发环境搭建

实际在选择虚拟机的时候纠结了要用virualbox还是vmware,初步比较结果: 1.virualbox能够使用vmware的硬盘格式,因此可以自由选择。 2.都能够实现主机和宿主机之间的文件夹共享。 3.virualbox是自由软件,vmware是商业软件。 在功能上…

STM32 入门教程(江科大教材)#笔记2

3-4按键控制LED /** LED.c**/ #include "stm32f10x.h" // Device headervoid LED_Init(void) {/*开启时钟*/RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //开启GPIOA的时钟/*GPIO初始化*/GPIO_InitTypeDef GPIO_InitStructure;GPIO_I…