架构思维: 数据一致性的两种场景深度解读

news2025/4/17 13:13:14

文章目录

  • Pre
  • 案例
  • 数据一致性问题的两种场景
    • 第一种场景:实时数据不一致不要紧,保证数据最终一致性就行
    • 第二种场景:必须保证实时一致性
  • 最终一致性方案
  • 实时一致性方案
    • TCC 模式
    • Seata 中 AT 模式的自动回滚
      • 一阶段
      • 二阶段-回滚
      • 二阶段-提交

在这里插入图片描述


Pre

架构思维:分布式事务一致性_基于 MQ 的可靠消息投递方案

分布式协同 - 分布式事务_TCC解决方案

分布式协同 - 分布式事务_2PC & 3PC解决方案

深入理解分布式技术 - 漫谈分布式事务及解决方案

分布式协同 - 分布式事务一二事儿

分布式 - AT、TCC、SAGA、XA四种分布式事务模型的工作原理和应用场景

深入理解分布式技术 - 分布式事务总结回顾

深入理解分布式技术 - TCC 事务模型及实战


案例

微服务架构, 很多时候我们往往需要跨多个服务去更新多个数据库的数据,类似下图所示的架构。

图1

在这里插入图片描述

如图1 所示,如果业务正常运转,3 个服务的数据应该变为 a2、b2、c2,此时数据才一致。但是如果出现网络抖动、服务超负荷或者数据库超负荷等情况,整个处理链条有可能在步骤二失败,这时数据就会变成 a2、b1、c1,当然也有可能在步骤三失败,最终数据就会变成 a2、b2、c1,这样数据就对不上了,即数据不一致。

此时,我们不得不抽出时间针对数据一致性问题给出一个完美解决方案。


数据一致性问题的两种场景

第一种场景:实时数据不一致不要紧,保证数据最终一致性就行

因为一些服务出现错误,导致图 1 的步骤三失败,此时处理完请求后,数据就变成了 a2、 b2、c1,不过不要紧,我们只需保证最终数据是 a2、b2、 c2 就行。


第二种场景:必须保证实时一致性

如果图 1 中的步骤二和步骤三成功了,数据就会变成 b2、c2,但是如果步骤三失败,那么步骤一和步骤二会立即回滚,保证数据变回 a1、b1。


针对以上两种具体的场景,其具体解决方案是什么呢?

最终一致性方案

对于数据要求最终一致性的场景,实现思路是这样的:

  • 每个步骤完成后,生产一条消息给 MQ,告知下一步处理接下来的数据;

  • 消费者收到这条消息后,将数据处理完成后,与步骤一一样触发下一步;

  • 消费者收到这条消息后,如果数据处理失败,这条消息应该保留,直到消费者下次重试。

在这里插入图片描述

关于上图,详细的实现逻辑如下:

  • 调用端调用 Service A;

  • Service A 将数据库中的 a1 改为 a2;

  • Service A 生成一条步骤 2(姑且命名为 Step2)的消息给到 MQ;

  • Service A 返回成功给调用端;

  • Service B 监听 Step2 的消息,拿到一条消息。

  • Service B 将数据库中的 b1 改为 b2;

  • Service B 生成一条步骤 3(姑且命名为 Step3)的消息给到 MQ;

  • Service B 将 Step2 的消息设置为已消费;

  • Service C 监听 Step3 的消息,拿到一条消息;

  • Service C 将数据库中的 c1 改为 c2;

  • Service C 将 Step3 的消息设置为已消费。

接下来我们考虑下,如果每个步骤失败了该怎么办?

  1. 调用端调用 Service A。

解决方案:如果这步失败,直接返回失败给用户,用户数据不受影响。

  1. Service A 将数据库中的 a1 改为 a2。

解决方案:如果这步失败,利用本地事务数据直接回滚就行,用户数据不受影响。

  1. Service A 生成一条步骤 2(姑且命名为 Step2)的消息给到 MQ。

解决方案:如果这步失败,利用本地事务数据将步骤 2 直接回滚就行,用户数据不受影响。

  1. Service A 返回成功给调用端。

解决方案:如果这步失败,不做处理。

  1. Service B 监听 Step2 的消息,拿到一条消息。

解决方案:如果这步失败,MQ 有对应机制,我们无须担心。

  1. Service B 将数据库中的 b1 改为 b2。

解决方案:如果这步失败,利用本地事务直接将数据回滚,再利用消息重试的特性重新回到步骤 5 。

  1. Service B 生成一条步骤 3(姑且命名为 Step3)的消息给到 MQ。

解决方案:如果这步失败,MQ 有生产消息失败重试机制。要是出现极端情况,服务器会直接挂掉,因为 Step2 的消息还没消费,MQ 会有重试机制,然后找另一个消费者重新从步骤 5 执行。

  1. Service B 将 Step2 的消息设置为已消费。

解决方案:如果这步失败,MQ 会有重试机制,找另一个消费者重新从步骤 5 执行。

  1. Service C 监听 Step3 的消息,拿到一条消息。

解决方案:如果这步失败,参考步骤 5 的解决方案。

  1. Service C 将数据库中的 c1 改为 c2。

解决方案:如果这步失败,参考步骤 6 的解决方案。

  1. Service C 将 Step3 的消息设置为已消费。

解决方案:如果这步失败,参考步骤 8 的解决方案。

以上就是最终一致性的解决方案.

如果仔细思考了该方案,可能會存在以下 2 点疑问。

  • 因为我们利用了 MQ 的重试机制,就有可能出现步骤 6 跟步骤 10 重复执行的情况,此时该怎么办?比如上面流程中的步骤 8 失败了,需要从步骤 5 重新执行,这时就会出现步骤 6 执行 2 遍的情况。为此,在下游(步骤 6 和 步骤 10)更新数据时,我们需要保证业务代码的幂等性 。

  • 如果每个业务流程都需要这样处理,岂不是需要额外写很多代码?那我们是否可以将类似处理流程的重复代码抽取出来?答案是可以的,这里使用的 MQ 相关逻辑在其他业务流程中也通用,最终就是将这些代码进行了抽取并封装。


实时一致性方案

实时一致性,其实就是我们常说的分布式事务。

MySQL 其实有一个两阶段提交的分布式事务方案(MySQL XA),但是该方案存在严重的性能问题。比如,一个数据库的事务与多个数据库间的 XA 事务性能可能相差 10 倍。另外,在 XA 的事务处理过程中它会长期占用锁资源,所以一开始并不考虑这个方案。

那时,市面上比较流行的方案是使用 TCC 模式,下面我们简单介绍一下。

TCC 模式

在 TCC 模式中,我们会把原来的一个接口分为 Try 接口、Confirm 接口、Cancel 接口。

在这里插入图片描述

  • Try 接口用来检查数据、预留业务资源。

  • Confirm 接口用来确认实际业务操作、更新业务资源。

  • Cancel 接口是指释放 Try 接口中预留的资源。

比如积分兑换折扣券的例子中需要调用账户服务减积分、营销服务加折扣券这两个服务,那么针对账户服务减积分这个接口,我们需要写 3 个方法,如下代码所示:

public boolean prepareMinus(BusinessActionContext businessActionContext, final String accountNo, final double amount) {    
   //校验账户积分余额
   //冻结积分金额
}


public boolean Confirm(BusinessActionContext businessActionContext) {    
   //扣除账户积分余额    
   //释放账户 冻结积分金额
}

public boolean Cancel(BusinessActionContext businessActionContext) {    
    //回滚所有数据变更
}

同样,针对营销服务加折扣券这个接口,我们也需要写3个方法,而后调用的大体步骤如下:

在这里插入图片描述
图 3 中绿色代表成功的调用路径,如果中间出错,就会先调用相关服务的回退方法,再进行手工回退。原本我们只需要在每个服务中写一段业务代码就行,现在需要拆成 3 段来写,而且还涉及以下 5 点注意事项:

  • 我们需要保证每个服务的 Try 方法执行成功后,Confirm 方法在业务逻辑上能够执行成功;

  • 可能会出现 Try 方法执行失败而 Cancel 被触发的情况,此时我们需要保证正确回滚;

  • 可能因为网络拥堵出现 Try 方法的调用被堵塞的情况,此时事务控制器判断 Try 失败并触发了 Cancel 方法,后来 Try 方法的调用请求到了服务这里,此时我们应该拒绝 Try 请求逻辑;

  • 所有的 Try、Confirm、Cancel 都需要确保幂等性;

  • 整个事务期间的数据库数据处于一个临时的状态,其他请求需要访问这些数据时,我们需要考虑如何正确被其他请求使用,而这种使用包括读取和并发的修改。

所以 TCC 模式是一个很麻烦的方案,除了每个业务代码的工作量 X3 之外,出错的概率也高,因为我们需要通过相应逻辑保证上面的注意事项都被处理。


后来,刚好看到了一篇介绍 Seata 的文章,了解到 AT 模式也能解决这个问题。

Seata 中 AT 模式的自动回滚

对于使用 Seata 的人来说操作比较简单,只需要在触发整个事务的业务发起方的方法中加入@GlobalTransactional 标注,且使用普通的 @Transactional 包装好分布式事务中相关服务的相关方法即可。

在 Seata 内在机制中,AT 模式的自动回滚往往需要执行以下步骤:

一阶段

  • 解析每个服务方法执行的 SQL,记录 SQL 的类型(Update、Insert 或 Delete),修改表并更新 SQL 条件等信息;

  • 根据前面的条件信息生成查询语句,并记录修改前的数据镜像;

  • 执行业务的 SQL;

  • 记录修改后的数据镜像;

  • 插入回滚日志:把前后镜像数据及业务 SQL 相关的信息组成一条回滚日志记录,插入 UNDO_LOG 表中;

  • 提交前,向 TC 注册分支,并申请相关修改数据行的全局锁 ;

  • 本地事务提交:业务数据的更新与前面步骤生成的 UNDO LOG 一并提交;

  • 将本地事务提交的结果上报给事务控制器。

二阶段-回滚

收到事务控制器的分支回滚请求后,我们会开启一个本地事务,并执行如下操作:

  • 查找相应的 UNDO LOG 记录;

  • 数据校验:拿 UNDO LOG 中的后镜像数据与当前数据进行对比,如果存在不同,说明数据被当前全局事务之外的动作做了修改,此时我们需要根据配置策略进行处理;

  • 根据 UNDO LOG 中的前镜像和业务 SQL 的相关信息生成回滚语句并执行;

  • 提交本地事务,并把本地事务的执行结果(即分支事务回滚的结果)上报事务控制器。

二阶段-提交

  • 收到事务控制器的分支提交请求后,我们会将请求放入一个异步任务队列中,并马上返回提交成功的结果给事务控制器。

  • 异步任务阶段的分支提交请求将异步地、批量地删除相应 UNDO LOG 记录。

以上就是 Seata 的 AT 模式的简单介绍。

在这里插入图片描述

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

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

相关文章

大数据knox网关API

我们过去访问大数据组件,如sparkui,hdfs的页面,以及yarn上面看信息是很麻烦的一件事。要记每个端口号,比如50070,8090,8088,4007,如果换到另一个集群,不同版本&#xff0…

【Tauri2】015——前端的事件、方法和invoke函数

目录 前言 正文 准备 关键url 获取所有命令 切换主题set_theme 设置大小 获得版本version 名字name 监听窗口移动 前言 【Tauri2】005——tauri::command属性与invoke函数-CSDN博客https://blog.csdn.net/qq_63401240/article/details/146581991?spm1001.2014.3001.…

密码学基础——分组密码的运行模式

前面的文章中文我们已经知道了分组密码是一种对称密钥密码体制,其工作原理可以概括为将明文消息分割成固定长度的分组,然后对每个分组分别进行加密处理。 下面介绍分组密码的运行模式 1.电码本模式(ECB) 2.密码分组链接模式&…

Python----计算机视觉处理(Opencv:道路检测完整版:透视变换,提取车道线,车道线拟合,车道线显示,)

Python----计算机视觉处理(Opencv:道路检测之道路透视变换) Python----计算机视觉处理(Opencv:道路检测之提取车道线) Python----计算机视觉处理(Opencv:道路检测之车道线拟合) Python----计算机视觉处理&#xff0…

基于飞桨框架3.0本地DeepSeek-R1蒸馏版部署实战

深度学习框架与大模型技术的融合正推动人工智能应用的新一轮变革。百度飞桨(PaddlePaddle)作为国内首个自主研发、开源开放的深度学习平台,近期推出的3.0版本针对大模型时代的开发痛点进行了系统性革新。其核心创新包括“动静统一自动并行”&…

docker初始环境搭建(docker、Docker Compose、portainer)

docker、Docker Compose和portainer的安装部署、使用 docker、Docker Compose和portainer的安装部署、使用一.安装docker1.失败的做法2.首先卸载旧版本(没安装则下一步)3.配置下载的yum来源,不然yum search搜不到4.安装启动docker5.替换国内源…

开源RuoYi AI助手平台的未来趋势

近年来,人工智能技术的迅猛发展已经深刻地改变了我们的生活和工作方式。 无论是海外的GPT、Claude等国际知名AI助手,还是国内的DeepSeek、Kimi、Qwen等本土化解决方案,都为用户提供了前所未有的便利。然而,对于那些希望构建属于自…

element-ui自制树形穿梭框

1、需求 由于业务特殊需求,想要element穿梭框功能,数据是二级树形结构,选中左边数据穿梭到右边后,左边数据不变。多次选中左边相同数据进行穿梭操作,右边数据会多次增加相同的数据。右边数据穿梭回左边时,…

Linux系统学习Day04 阻塞特性,文件状态及文件夹查询

知识点4【文件的阻塞特性】 文件描述符 默认为 阻塞 的 比如:我们读取文件数据的时候,如果文件缓冲区没有数据,就需要等待数据的到来,这就是阻塞 当然写入的时候,如果发现缓冲区是满的,也需要等待刷新缓…

Python基础——Pandas库

对象的创建 导入 Pandas 时,通常给其一个别名“pd”,即 import pandas as pd。作为标签库,Pandas 对象在 NumPy 数组基础上给予其行列标签。可以说,列表之于字典,就如 NumPy 之于 Pandas。Pandas 中,所有数…

C++: 类型转换

C: 类型转换 (一)C语言中的类型转换volatile关键字 修饰const变量 (二)C四种强制类型转换1. static_cast2. reinterpret_cast3. const_cast4. dynamic_cast总结 (三)RTTI (一)C语言中的类型转换 在C语言中…

STM32——DAC转换

DAC简介 DAC,全称:Digital-to-Analog Converter,扑指数字/模拟转换器 ADC和DAC是模拟电路与数字电路之间的桥梁 DAC的特性参数 1.分辨率: 表示模拟电压的最小增量,常用二进制位数表示,比如&#xff1a…

Kafka的索引设计有什么亮点

想获取更多高质量的Java技术文章?欢迎访问Java技术小馆官网,持续更新优质内容,助力技术成长 Java技术小馆官网https://www.yuque.com/jtostring Kafka的索引设计有什么亮点? Kafka 之所以能在海量数据的传输和处理过程中保持高…

在深度学习中,如何统计模型的 ​​FLOPs(浮点运算次数)​​ 和 ​​参数量(Params)

在深度学习中,统计模型的FLOPs(浮点运算次数)和参数量(Params)是评估模型复杂度和计算资源需求的重要步骤。 一、参数量(Params)计算 参数量指模型中所有可训练参数的总和,其计算与…

Linux之Shell脚本--命令提示的写法

原文网址:Linux之Shell脚本--命令提示的写法-CSDN博客 简介 本文介绍Linux的Shell脚本命令提示的写法。 场景描述 在写脚本时经常会忘记怎么使用,需要进行命令提示。比如:输入-h参数,能打印用法。 实例 新建文件&#xff1a…

Day19 -实例:xcx逆向提取+微信开发者工具动态调试+bp动态抓包对小程序进行资产收集

思路: 拿到源码后的测试方向: Step1、xcx逆向提取源码 00x1 先将曾经使用小程序记录删除 00x2 访问小程序 例:汉川袁老四小程序 00x3 将文件给xcx进行逆向解包 xcx工具的目录下,wxpack文件夹内 Step2、微信开发者工具进行动态…

鸿蒙Arkts开发飞机大战小游戏,包含无敌模式,自动射弹,暂停和继续

飞机大战可以把飞机改成图片,目前包含无敌模式,自动射弹,暂停和继续的功能 代码如下: // 定义位置类 class GamePosition {x: numbery: numberconstructor(x: number, y: number) {this.x xthis.y y} }Entry Component struct…

从基础算力协作到超智融合,超算互联网助力大语言模型研习

一、背景 大语言模型(LLMs)的快速发展释放出了AI应用领域的巨大潜力。同时,大语言模型作为 AI领域的新兴且关键的技术进展,为 AI 带来了全新的发展方向和应用场景,给 AI 注入了新潜力,这体现在大语言模型独…

M1使用docker制作镜像xxl-job,供自己使用

很苦逼一个情况,m1的docker假如不翻墙,我们找不到xxl-job,所以我们要自己制作 首先先去下载xxl-job源码https://gitee.com/xuxueli0323/xxl-job 你把它拉去到idea中 拉去成功后,进入这个xxl-job目录 执行 mvn clean package -Dmaven.test.skiptrue(这一步…

第一个简易SSM框架项目

引言 这是一个简易SSM整合项目,适合后端入门的练习项目,其中没有太多的业务操作,主要是这个框架,以及编码的顺序,希望大家有所收获 首先需要先配置环境 数据库环境 创建一个存放书籍的数据库表 create database s…