基于本地消息表实现分布式事务(最终一致性)
前言
传统单体架构下,所有的功能模块都在一个应用下,所有的代码和业务逻辑都在同一个应用下实现,所以保证数据的一致性就很简单,保证相关操作都在同一个本地事务下就可以了。
但是在微服务架构下,将一个应用拆分成了多个独立的服务,每个服务都能有自己的数据库,服务间通信都是通过远程调用实现,实现一个功能可能需要由几个不同的服务来共同实现。这就会带来一个问题,不同的服务之间无法做到使用同一个事务,这就无法保证数据的一致性了。
解决分布式事务的问题,一劳永逸的方式就是直接使用 Seata,Seata 是一个开源的分布式事务解决方案,用于解决分布式系统中的数据一致性问题。但是,引入 Seata 实在是太重了,在实际工作中接触过的系统,并没有那么多的业务需要使用到分布式事务,为了解决那么一两个业务的问题却要为整个系统引入分布式事务服务,代价实在是太大了。而且,相关的业务实际上只需要保证数据的最终一致性,不用保证强一致性,所以在实践可以使用本地消息表的方案来解决分布式事务问题。
本地消息表方案
如果系统中只有少数服务需要用到分布式事务,那么直接在该服务下创建一张本地消息表,结合消息队列,就能够实现数据的最终一致性了。
表设计
本地消息表的设计如下
| 字段 | 类型 | 注释 |
| id | long | id |
| msg_type | varchar | 消息类型 |
| biz_id | varchar | 业务唯一标志 |
| content | text | 消息体 |
| state | varchar | 状态(待发送,已消费) |
| create_time | datetime | 创建时间 |
| update_time | datetime | 更新时间 |
时序图

plantuml
@startuml 'https://plantuml.com/sequence-diagramautonumberparticipant Scheduled_Task database DB_A participant Service_A queue Queue participant Service_B database DB_Bgroup Service_A本地事务DB_A <- Service_A: 写本地业务数据DB_A <- Service_A: 写本地消息数据 endService_A -> Queue: 发送消息 note left 发送消息放到事务外 防止由于网络延迟出现 Service_A认为发送失败导致事务回滚 而实际上MQ收到了消息 Service_B也消费了消息 从而出现数据不一致的情况 end noteQueue -> Service_B: 消费消息Service_B -> Service_B: 幂等判断 note right 防止重复消费消息 end notegroup Service_B本地事务Service_B -> DB_B: 写本地业务数据 endgroup 更新本地消息表状态Scheduled_Task <- Service_B: 更新本地消息表状态Scheduled_Task -> DB_A: 更新本地消息表状态 end note right 这里即使失败了也无所谓 两个服务的数据已经一致了 没有更新消息状态,定时任务会重新投递 Service_B做好幂等处理 等下次消费再去修改消息状态即可 end noteloop 定时任务Scheduled_Task -> DB_A: 查询未成功投递的消息Scheduled_Task -> Queue: 重新投递 end@enduml
消息服务方案
本地消息表只只用系统中只有少量业务需要实现分布式事务的情况,如果系统中的绝大多数服务都存在分布式事务的业务场景,为每个服务都创建一张本地消息表显然很麻烦,难以维护。
用到的地方多,实际上就可以考虑上 Seata 了,专业的事要交给专业的服务来做。不过这里也可以改进一下本地消息表的方案,增加一个专门处理分布式事务消息的消息服务和消息库。
表设计
表设计同上面一样,消息服务只不过将所有本地消息表合并成一张表了
只不过 state 字段需要多一个状态来区分
在本地消息表方案中,由于写Service_A业务和创建消息是在同一个事务中,它们要么同时成功,要么同时失败,所以两个状态(待发送,已消费)就能表示整个消息的生命周期。
- 待发送状态,则表示Service_A业务和消息同时写入库成功,等待Service_B消费消息
- 已消费状态,则表示Service_B已经成功消费了消息
在消息服务方案中,写业务和创建消息不在同一个事务中,所以需要再加一个状态(发送中)
- 待发送状态,则表示刚创建消息,此时Service_A还没有写库
- 发送中状态:则表示Service_A已经写库成功,等待Service_B消费消息
- 已消费状态,则表示Service_B已经成功消费了消息
时序图

plantuml
@startuml 'https://plantuml.com/sequence-diagramautonumberdatabase DB_A participant Service_A participant Service_Msg database DB_Msg queue Queue participant Service_B database DB_Bgroup 创建消息Service_A -> Service_Msg: 创建消息note left: 生成业务idService_Msg -> DB_Msg: 创建消息note left: 消息状态:待发送Service_A <- Service_Msg: 返回消息ID endgroup Service_A本地事务DB_A <- Service_A: 写本地业务数据 endService_A -> Service_Msg: 发送消息 note left: 携带消息ID Service_Msg -> DB_Msg: 更新消息状态 note right: 消息状态:发送中 Service_Msg -> Queue: 发送消息 Queue -> Service_B: 消费消息Service_B -> Service_B: 幂等判断group Service_B本地事务Service_B -> DB_B: 写本地业务数据 endgroup 更新消息状态Service_Msg <- Service_B: 更新消息状态Service_Msg -> DB_Msg: 更新消息状态note right: 消息状态:已消费 endloop 定时任务Service_Msg -> DB_Msg: 查询状态为发送中的消息Service_Msg -> Queue: 重新投递 end@enduml
相关文章:
基于本地消息表实现分布式事务(最终一致性)
前言 传统单体架构下,所有的功能模块都在一个应用下,所有的代码和业务逻辑都在同一个应用下实现,所以保证数据的一致性就很简单,保证相关操作都在同一个本地事务下就可以了。 但是在微服务架构下,将一个应用拆分成了…...
大数据mapper书写范式hdfs
文章目录 1. 大数据mapper书写范式hdfs 1. 大数据mapper书写范式hdfs import json import sysdef read_input(input_stream):for line in input_stream:yield line.rstrip(\n)def load_json_data(json_line):try:data json.loads(json_line)unique_id data.get(id)combined_…...
ubuntu将软件放到任务栏
右键点击这个 pycharm 方法1: 方法2: sudo nano /usr/share/applications/PyCharm.desktop 编辑这个 [Desktop Entry] NamePyCharm CommentPyCharm Integrated Development Environment Exec/path/to/PyCharm.sh Icon/path/to/PyCharm.svg Terminalf…...
Spring Boot 参数校验 Validation 使用
概述 当我们想提供可靠的 API 接口,对参数的校验,以保证最终数据入库的正确性,是必不可少的活。前、后端校验都是保证参数的准确性的手段之一,前端校验并不安全,任何人都可以通过接口来调用我们的服务,就算…...
基于el-table的表格点选和框选功能
开篇 本篇文章旨在实现一个基于el-table的表格点选和框选功能,除此之外,还支持多种模式的切换、自定义勾选日期等。且,该表格后续可能还会持续优化! 功能介绍 表格点选和框选功能(没有点击ctrl键的情况下)…...
LabVIEW压电陶瓷阻抗测试系统
开发了一种基于LabVIEW软件与PXI模块化仪器的压电陶瓷阻抗测试系统。该系统能在高电压工作条件下测量压电陶瓷的阻抗特性,包括阻抗模值与阻抗角的频率特性,为压电陶瓷的进一步分析与应用提供了重要参考。 项目背景 现有的阻抗测试仪大多只能在低电压条件…...
电销机器人能大幅度提升效率
1、安全稳定性能好 营销机器人的稳定性非常强,在使用性能方面会有更好的优势,而且用的过程中也可以不断的这些模块更新和功能升级,所以会不断的满足大家更多的使用要求,在操作使用的时候非常简单和方便,直接就可以给客…...
虚拟机能访问网页但ping不通百度
最近遇到了奇怪的问题,虚拟机能访问网页,但ping不通百度,记录一下问题的排查过程。 能访问网页,说明DNS、TCP和HTTP没有问题,ping不通,说明ICMP应该出了问题。 首先通过traceroute追踪报文的转发过程&…...
RK3588开发笔记-buildroot编译配置
目录 前言 一、buildroot简介 二、buildroot配置编译 buildroot config配置 buildroot 编译 buildroot 如何单独编译某个软件包 何时需要完全重建 如何完全重建 总结 前言 Rockchip RK3588 是一款强大的多核处理器,广泛应用于边缘计算、人工智能、嵌入式系统等领域。为了在…...
Java设计模式(适配器模式)
定义 将一个类的接口转换成客户希望的另一个接口。适配器模式让那些接口不兼容的类可以一起工作。 角色 目标抽象类(Target):目标抽象类定义客户所需的接口(在类适配器中,目标抽象类只能是接口)。 适配器类…...
机器学习框架巅峰对决:TensorFlow vs. PyTorch vs. Scikit-Learn实战分析
1.引言 1.1机器学习框架的重要性 在机器学习的黄金时代,框架的选择对于开发高效、可扩展的模型至关重要。合适的框架可以极大地提高开发效率,简化模型的构建和训练过程,并支持大规模的模型部署。因此,了解和选择最合适的机器学习…...
基于STM32的智能窗帘控制系统
目录 引言环境准备工作 硬件准备软件安装与配置系统设计 系统架构硬件连接代码实现 初始化代码控制代码应用场景 家居智能窗帘控制办公室窗帘自动调节常见问题及解决方案 常见问题解决方案结论 1. 引言 智能窗帘控制系统能够通过时间、光照强度或远程控制,实现对…...
【算法】普里姆算法解决修路问题
应用场景——修路问题 1.某地有 7 个村庄(A,B,C,D,E,F,G),现在需要修路把 7 个村庄连通 2.各个村庄的距离用边线表示(权),比如 A - …...
Python 之Scikit-learn(二) -- Scikit-learn标准化数据
在机器学习中,数据标准化是一项关键的预处理步骤。标准化(Standardization)是将数据转换为具有均值为0和标准差为1的分布。这样可以确保特征在相同的尺度上,有助于提升某些机器学习算法的性能和稳定性。 Scikit-learn提供了一个简…...
机械学习—零基础学习日志(python编程)
零基础为了学人工智能,正在艰苦的学习 昨天给高等数学的学习按下暂停键,现在开始学习python编程。 我学习的思路是直接去阿里云的AI学习课堂里面学习。 整体感觉,阿里云的AI课堂还是有一些乱,早期课程和新出内容没有更新和归档…...
WEB应用(十三)---RCE
什么是RCE? Remote Command/Code Execute,远程命令或代码执行。通过构造特殊的字符串,将数据提交至Web应用程序,并利用该方式执行外部程序或系统命令实施攻击,类似于SQL注入。 Web应用程序使用了一些可以执行系统命令或…...
【云原生】Service服务暴露详细
Service服务 文章目录 Service服务一、Service介绍1.1、介绍1.2、Kubernetes中的Service 二、Service服务类型2.1、ClusterIP2.2、NodePort2.3、LadBalancer2.4、ExternalName 三、Service玩法3.1、定义Service3.2、端口定义别名3.3、多端口Service 四、Service类型4.1、Cluste…...
实名认证次数限制
在业务层实现实名认证次数限制 这个功能是通过以下步骤实现实名认证的次数限制: 每日失败尝试次数限制:限制用户每天可以尝试失败的次数。失败后的冷却时间:用户在连续失败几次后需要等待一段时间才能再次尝试。成功认证后的限制࿱…...
【如何在Python中使用pathlib模块】
在Python中使用pathlib模块主要涉及创建Path对象,并利用这些对象提供的方法来执行文件系统的各种操作。以下是一些详细的步骤和示例,帮助你了解如何在Python中有效地使用pathlib模块。 1. 导入Path类 首先,从pathlib模块中导入Path类。 fr…...
sqli-labs第一关详细解答
首先判断是否有注入点 发现and 11 和 and 12结果一样,所以应该是字符型注入,需要对单引号做闭合 做闭合后发现报错,提示Limit 0,1,那就说明存在注入点,但是要注释掉后面的limit 0,1 使用--注释掉limit 0,1后ÿ…...
解决Ubuntu22.04 VMware失败的问题 ubuntu入门之二十八
现象1 打开VMware失败 Ubuntu升级之后打开VMware上报需要安装vmmon和vmnet,点击确认后如下提示 最终上报fail 解决方法 内核升级导致,需要在新内核下重新下载编译安装 查看版本 $ vmware -v VMware Workstation 17.5.1 build-23298084$ lsb_release…...
系统设计 --- MongoDB亿级数据查询优化策略
系统设计 --- MongoDB亿级数据查询分表策略 背景Solution --- 分表 背景 使用audit log实现Audi Trail功能 Audit Trail范围: 六个月数据量: 每秒5-7条audi log,共计7千万 – 1亿条数据需要实现全文检索按照时间倒序因为license问题,不能使用ELK只能使用…...
2025盘古石杯决赛【手机取证】
前言 第三届盘古石杯国际电子数据取证大赛决赛 最后一题没有解出来,实在找不到,希望有大佬教一下我。 还有就会议时间,我感觉不是图片时间,因为在电脑看到是其他时间用老会议系统开的会。 手机取证 1、分析鸿蒙手机检材&#x…...
Angular微前端架构:Module Federation + ngx-build-plus (Webpack)
以下是一个完整的 Angular 微前端示例,其中使用的是 Module Federation 和 npx-build-plus 实现了主应用(Shell)与子应用(Remote)的集成。 🛠️ 项目结构 angular-mf/ ├── shell-app/ # 主应用&…...
GO协程(Goroutine)问题总结
在使用Go语言来编写代码时,遇到的一些问题总结一下 [参考文档]:https://www.topgoer.com/%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B/goroutine.html 1. main()函数默认的Goroutine 场景再现: 今天在看到这个教程的时候,在自己的电…...
Git常用命令完全指南:从入门到精通
Git常用命令完全指南:从入门到精通 一、基础配置命令 1. 用户信息配置 # 设置全局用户名 git config --global user.name "你的名字"# 设置全局邮箱 git config --global user.email "你的邮箱example.com"# 查看所有配置 git config --list…...
LabVIEW双光子成像系统技术
双光子成像技术的核心特性 双光子成像通过双低能量光子协同激发机制,展现出显著的技术优势: 深层组织穿透能力:适用于活体组织深度成像 高分辨率观测性能:满足微观结构的精细研究需求 低光毒性特点:减少对样本的损伤…...
C语言中提供的第三方库之哈希表实现
一. 简介 前面一篇文章简单学习了C语言中第三方库(uthash库)提供对哈希表的操作,文章如下: C语言中提供的第三方库uthash常用接口-CSDN博客 本文简单学习一下第三方库 uthash库对哈希表的操作。 二. uthash库哈希表操作示例 u…...
「全栈技术解析」推客小程序系统开发:从架构设计到裂变增长的完整解决方案
在移动互联网营销竞争白热化的当下,推客小程序系统凭借其裂变传播、精准营销等特性,成为企业抢占市场的利器。本文将深度解析推客小程序系统开发的核心技术与实现路径,助力开发者打造具有市场竞争力的营销工具。 一、系统核心功能架构&…...
JDK 17 序列化是怎么回事
如何序列化?其实很简单,就是根据每个类型,用工厂类调用。逐个完成。 没什么漂亮的代码,只有有效、稳定的代码。 代码中调用toJson toJson 代码 mapper.writeValueAsString ObjectMapper DefaultSerializerProvider 一堆实…...
