「微服务」微服务架构中的数据一致性
在微服务中,一个逻辑上原子操作可以经常跨越多个微服务。即使是单片系统也可能使用多个数据库或消息传递解决方案。使用多个独立的数据存储解决方案,如果其中一个分布式流程参与者出现故障,我们就会面临数据不一致的风险 - 例如在未下订单的情况下向客户收费或未通知客户订单成功。在本文中,我想分享一些我为使微服务之间的数据最终保持一致而学到的技术。
为什么实现这一目标如此具有挑战性?只要我们有多个存储数据的地方(不在单个数据库中),就不能自动解决一致性问题,工程师在设计系统时需要注意一致性。目前,在我看来,业界还没有一个广为人知的解决方案,可以在多个不同的数据源中自动更新数据 - 我们可能不应该等待很快就能获得一个。
以自动且无障碍的方式解决该问题的一种尝试是实现两阶段提交(2PC)模式的XA协议。但在现代高规模应用中(特别是在云环境中),2PC似乎表现不佳。为了消除2PC的缺点,我们必须交易ACID for BASE并根据要求以不同方式覆盖一致性问题。
Saga模式
在多个微服务中处理一致性问题的最着名的方法是Saga模式。 您可以将Sagas视为多个事务的应用程序级分布式协调。根据用例和要求,您可以优化自己的Saga实施。相反,XA协议试图涵盖所有场景。Saga模式也不是新的。它在过去已知并用于ESB和SOA体系结构中。最后,它成功地转变为微服务世界。跨越多个服务的每个原子业务操作可能包含技术级别的多个事务。Saga Pattern的关键思想是能够回滚其中一个单独的交易。众所周知,开箱即用的已经提交的单个事务无法进行回滚。但这是通过引入补偿操作来实现的 - 通过引入“取消”操作。
除了取消之外,您还应该考虑使您的服务具有幂等性,以便在出现故障时重试或重新启动某些操作。应监控故障,并应积极主动地应对故障。
对账
如果在进程的中间负责调用补偿操作的系统崩溃或重新启动,该怎么办?在这种情况下,用户可能会收到错误消息,并且应该触发补偿逻辑,或者 - 当处理异步用户请求时,应该恢复执行逻辑。
要查找崩溃的事务并恢复操作或应用补偿,我们需要协调来自多个服务的数据。对账
是在金融领域工作的工程师所熟悉的技术。你有没有想过银行如何确保你的资金转移不会丢失,或者两个不同的银行之间如何汇款?快速回答是对账。
在会计中,对账是确保两组记录(通常是两个账户的余额)达成一致的过程。对帐用于确保离开帐户的资金与实际支出的资金相匹配。这是通过确保在特定会计期间结束时余额匹配来完成的。- Jean Scheid,“了解资产负债表账户调节”,Bright Hub,2011年4月8日
回到微服务,使用相同的原则,我们可以在一些动作触发器上协调来自多个服务的数据。当检测到故障时,可以按计划或由监控系统触发操作。最简单的方法是运行逐记录比较。可以通过比较聚合值来优化该过程。在这种情况下,其中一个系统将成为每条记录的真实来源。
事件簿
想象一下多步骤交易。如何在对帐期间确定哪些事务可能已失败以及哪些步骤失败?一种解决方案是检查每个事务的状态。在某些情况下,此功能不可用(想象一下发送电子邮件或生成其他类型消息的无状态邮件服务)。在其他一些情况下,您可能希望立即了解事务状态,尤其是在具有许多步骤的复杂方案中。例如,预订航班,酒店和转机的多步订单。
复杂的分布式流程
在这些情况下,事件日志可以提供帮助。记录是一种简单但功能强大的技术。许多分布式系统依赖于日志。“预写日志记录”是数据库在内部实现事务行为或维护副本之间一致性的方式。相同的技术可以应用于微服务设计。在进行实际数据更改之前,服务会写入有关其进行更改的意图的日志条目。实际上,事件日志可以是协调服务所拥有的数据库中的表或集合。
事件日志不仅可用于恢复事务处理,还可用于为系统用户,客户或支持团队提供可见性。但是,在简单方案中,服务日志可能是冗余的,状态端点或状态字段就足够了。
编配(Orchestration)与编排(choreography)
到目前为止,您可能认为sagas只是编配(orchestration )方案的一部分。但是sagas也可以用于编排(choreography ),每个微服务只知道过程的一部分。Sagas包括处理分布式事务的正流和负流的知识。在编排(choreography )中,每个分布式事务参与者都具有这种知识。
单次写入事件
到目前为止描述的一致性解决方案并不容易。他们确实很复杂。但有一种更简单的方法:一次修改一个数据源。我们可以将这两个步骤分开,而不是改变服务的状态并在一个过程中发出事件。
更改为先
在主要业务操作中,我们修改自己的服务状态,而单独的进程可靠地捕获更改并生成事件。这种技术称为变更数据捕获(CDC)。实现此方法的一些技术是Kafka Connect或Debezium。
使用Debezium和Kafka Connect更改数据捕获
但是,有时候不需要特定的框架。一些数据库提供了一种友好的方式来拖尾其操作日志,例如MongoDB Oplog。如果数据库中没有此类功能,则可以通过时间戳轮询更改,或使用上次处理的不可变记录ID查询更改。避免不一致的关键是使数据更改通知成为一个单独的过程。在这种情况下,数据库记录是单一的事实来源。只有在首先发生变化时才会捕获更改。
无需特定工具即可更改数据捕获
更改数据捕获的最大缺点是业务逻辑的分离。更改捕获过程很可能与更改逻辑本身分开存在于您的代码库中 - 这很不方便。最知名的变更数据捕获应用程序是与域无关的变更复制,例如与数据仓库共享数据。对于域事件,最好采用不同的机制,例如明确发送事件。
事件第一
让我们来看看颠倒的单一事实来源。如果不是先写入数据库,而是先触发一个事件,然后与自己和其他服务共享。在这种情况下,事件成为事实的唯一来源。这将是一种事件源的形式,其中我们自己的服务状态有效地成为读取模型,并且每个事件都是写入模型。
事件优先方法
一方面,它是一个命令查询责任隔离(CQRS)模式,我们将读取和写入模型分开,但CQRS本身并不关注解决方案中最重要的部分 - 使用多个服务来消耗事件。
相比之下,事件驱动的体系结构关注于多个系统所消耗的事件,但并未强调事件是数据更新的唯一原子部分。所以我想引入“事件优先”作为这种方法的名称:通过发出单个事件来更新微服务的内部状态 - 包括我们自己的服务和任何其他感兴趣的微服务。
“事件优先”方法面临的挑战也是CQRS本身的挑战。想象一下,在下订单之前,我们想要检查商品的可用性。如果两个实例同时收到同一项目的订单怎么办?两者都将同时检查读取模型中的库存并发出订单事件。如果没有某种覆盖方案,我们可能会遇到麻烦。
处理这些情况的常用方法是乐观并发:将读取模型版本放入事件中,如果读取模型已在消费者端更新,则在消费者端忽略它。另一种解决方案是使用悲观并发控制,例如在检查项目可用性时为项目创建锁定。
“事件优先”方法的另一个挑战是任何事件驱动架构的挑战 - 事件的顺序。多个并发消费者以错误的顺序处理事件可能会给我们带来另一种一致性问题,例如处理尚未创建的客户的订单。
诸如Kafka或AWS Kinesis之类的数据流解决方案可以保证将按顺序处理与单个实体相关的事件(例如,仅在创建用户之后为客户创建订单)。例如,在Kafka中,您可以按用户ID对主题进行分区,以便与单个用户相关的所有事件将由分配给该分区的单个使用者处理,从而允许按顺序处理它们。相反,在Message Brokers中,消息队列具有一个订单,但是多个并发消费者在给定顺序中进行消息处理(如果不是不可能的话)。在这种情况下,您可能会遇到并发问题。
实际上,在需要线性化的情况下或在具有许多数据约束的情况(例如唯一性检查)中,难以实现“事件优先”方法。但它在其他情况下确实很有用。但是,由于其异步性质,仍然需要解决并发和竞争条件的挑战。
设计一致性
有许多方法可以将系统拆分为多个服务。我们努力将单独的微服务与单独的域匹配。但域名有多细化?有时很难将域与子域或聚合根区分开来。没有简单的规则来定义您的微服务拆分。
我建议务实并考虑设计方案的所有含义,而不是只关注领域驱动的设计。其中一个影响是微服务隔离与事务边界的对齐情况。事务仅驻留在微服务中的系统不需要上述任何解决方案。在设计系统时我们一定要考虑事务边界。在实践中,可能很难以这种方式设计整个系统,但我认为我们应该致力于最大限度地减少数据一致性挑战。
接受不一致
虽然匹配帐户余额至关重要,但有许多用例,其中一致性不那么重要。想象一下,为分析或统计目的收集数据。即使我们从系统中随机丢失了10%的数据,也很可能不会影响分析的业务价值。
与事件共享数据
选择哪种解决方案
数据的原子更新需要两个不同系统之间达成共识,如果单个值为0或1则达成协议。当涉及到微服务时,它归结为两个参与者之间的一致性问题,并且所有实际解决方案都遵循一条经验法则:
在给定时刻,对于每个数据记录,您需要找到系统信任的数据源
事实的来源可能是事件,数据库或其中一项服务。实现微服务系统的一致性是开发人员的责任。我的方法如下:
-
尝试设计一个不需要分布式一致性的系统。不幸的是,对于复杂的系统来说,这几乎是不可能的。
-
尝试通过一次修改一个数据源来减少不一致的数量。
-
考虑事件驱动的架构。除了松散耦合之外,事件驱动架构的强大优势是通过将事件作为单一事实来源或由于更改数据捕获而产生事件来实现数据一致性的自然方式。
-
更复杂的场景可能仍然需要服务,故障处理和补偿之间的同步调用。知道有时候你可能需要在之后进行调和。
-
设计您的服务功能是可逆的,决定如何处理故障情况并在设计阶段早期实现一致性。
本文 :https://architect.pub/data-consistency-microservices-architecture | ||
讨论:知识星球【首席架构师圈】或者加微信小号【ca_cto】或者加QQ群【792862318】 | ||
公众号 | 【jiagoushipro】 【架构师酒馆】 精彩图文详解架构方法论,架构实践,技术原理,技术趋势。 我们在等你,赶快扫描关注吧。 | |
微信小号 | 【ca_cea】 50000人社区,讨论:企业架构,云计算,大数据,数据科学,物联网,人工智能,安全,全栈开发,DevOps,数字化. | |
视频号 | 【架构师酒馆】 1分钟快速了解架构相关的基本概念,模型,方法,经验。 每天1分钟,架构心中熟。 | |
知识星球 | 【首席架构师圈】向大咖提问,近距离接触,或者获得私密资料分享。 | |
喜马拉雅 | 【超级架构师】路上或者车上了解最新黑科技资讯,架构心得。 | 【智能时刻,架构君和你聊黑科技】 |
微博 | 【架构师酒馆】 | 智能时刻 |
哔哩哔哩 | 【架构师酒馆】 | |
抖音 | 【cea_cio】架构师酒馆 | |
小红书 | 【cea_csa_cto】架构师酒馆 | |
网站 | CIO(首席信息官) | https://cio.ceo |
网站 | CIO,CTO和CDO | https://cioctocdo.com |
网站 | 架构师实战分享 | https://architect.pub |
网站 | 程序员云开发分享 | https://pgmr.cloud |
官网 | 行天智能科技咨询公司 | https://xingtian.ai |
网站 | 开发者闲谈 | https://blog.developer.chat |
网站 | 首席隐私官内参 | https://cpo.work |
网站 | 首席安全官内参 | https://cso.pub |
网站 | CIO内参 | https://cio.cool |
网站 | CDO内参 | https://cdo.fyi |
网站 | CXO内参 | https://cxo.pub |
网站 | 首席架构师社区 | https://jiagoushi.pro |
谢谢大家关注,转发,点赞和点在看。
相关文章:

「微服务」微服务架构中的数据一致性
在微服务中,一个逻辑上原子操作可以经常跨越多个微服务。即使是单片系统也可能使用多个数据库或消息传递解决方案。使用多个独立的数据存储解决方案,如果其中一个分布式流程参与者出现故障,我们就会面临数据不一致的风险 - 例如在未下订单的情…...

ARCGIS PRO SDK 要素空间关系
一、要素与要素查询,返回的是bool值 1、 Touches 判断几何要素是否接触 Touches 如果 geometry1 与 geometry2 接触,则返回 true,否则 false。 touches GeometryEngine.Instance.Touches(Geometry1, Geometry2) 2、…...

Python面向对象高级与Python的异常、模块以及包管理
Python面向对象高级与Python的异常、模块以及包管理 一、Python中的继承 1、什么是继承 我们接下来来聊聊Python代码中的“继承”:类是用来描述现实世界中同一组事务的共有特性的抽象模型,但是类也有上下级和范围之分,比如:生物 => 动物 => 哺乳动物 => 灵长型…...

Python 爬取 哔站视频弹幕 并实现词云图可视化
嗨喽,大家好呀~这里是爱看美女的茜茜呐 环境介绍: python 3.8 解释器 pycharm 编辑器 第三方模块: requests >>> pip install requests protobuf >>> pip install protobuf 如何安装python第三方模块: win R 输入 cmd 点击确定, 输入安装命…...
BP神经网络详细原理,BP神经网络训练界面详解,基于BP神经网络的公司财务风险分类
目录 摘要 BP神经网络参数设置及各种函数选择 参数设置 训练函数 传递函数 学习函数 性能函数 显示函数 前向网络创建函数 BP神经网络训练窗口详解 训练窗口例样 训练窗口四部详解 基于BP神经网络的公司财务风险分类 完整代码下载链接:基于BP神经网络的公司财务风险分类(代码…...

C++ DAY1 作业
1.定义自己的命名空间myspace,并在myspace中定义一个字符串,并实现求字符串长度 #include <iostream>using namespace std; namespace myspace {string str;int length_fun(){getline(cin,str);int i 0;while(str[i] ! \0){i;}return i;}} using…...

「微服务模式」七种微服务反模式
什么是微服务 流行语经常为进化的概念提供背景,并且需要一个良好的“标签”来促进对话。微服务是一个新的“标签”,它定义了我个人一直在发现和使用的领域。文章和会议描述了一些事情,我慢慢意识到,过去几年我一直在发展自己的个人…...

运动耳机哪款性价比最高、性价比最高的运动耳机推荐
近年来,运动蓝牙耳机备受欢迎,成为人们健身时的必备时尚单品。随着蓝牙耳机的不断发展,市场上可供选择的产品种类繁多,因此挑选一款适合自己的蓝牙耳机并不困难。然而,并非每款耳机都适合户外或者运动场景下的使用&…...

FreeRTOS软件定时器
一、简介 二、实验 //创建一个单次定时器和一个周期定时器,打开两个定时器然后等待10s关闭定时器,此时会发现单次定时器打印1次停止,周期定时器打印5次停止 #include "FreeRTOS_demo.h"#define START_TASK_PRIO 1 #define…...
【Java集合类不安全示例】
文章目录 一、List二、Set三、Map 提示:以下是本篇文章正文内容,下面案例可供参考 一、List 代码如下(示例): public class ZZZZZZZZZZ {public static void main(String[] args) {// ArrList 非线程安全的集合List&l…...

cpp_07_类型转换构造_析构函数_深拷贝_静态成员
1 类型转换构造函数 1.1 why? 基本类型之间的转换,编译器内置转换规则:int -> double 类类型之间的转换,编译器不知道转换规则,需要用户提供:Cat -> Dog // consconv_why.cpp 为什么需要自定义转换 #includ…...
Java 已死、前端已凉?
文章目录 Java 的现状前端技术的现状分析结论 关于“Java 已死、前端已凉”的言论,这种说法更多地反映了行业对技术趋势的一种情绪化反应,而不一定是基于事实的判断。下面我来具体分析这个话题。 Java 的现状 Java 的普及与稳定性:Java 作为一…...
Calico IP_AUTODETECTION_METHOD
在 Calico 中,IP_AUTODETECTION_METHOD 的配置项用于指定 Calico 如何检测容器的 IP 地址。 一、kubernetes-internal-ip模式 其中,kubernetes-internal-ip 是一种特殊的模式,用于在 Kubernetes 环境中检测容器的 IP 地址。具体作用如下&…...

百分点科技成为中国“数据要素×”生态合作伙伴
12月24日,由中国经济体制改革研究会、中国电子、郑州市人民政府、中国经济改革研究基金会联合主办的中国“数据要素”生态大会在郑州召开,百分点科技受邀出席,并获颁中国“数据要素x”2024年度生态伙伴合作证书。 大会邀请了国家数据局党组成…...

智能优化算法应用:基于孔雀算法3D无线传感器网络(WSN)覆盖优化 - 附代码
智能优化算法应用:基于孔雀算法3D无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用:基于孔雀算法3D无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.孔雀算法4.实验参数设定5.算法结果6.参考文献7.MA…...
Redis自动部署脚本编写
#!/bin/bash ck_ok() { if [ $? -ne 0 ] then echo "$1 error." exit 1 fi } download_redis() { cd /usr/local/src if [ -f redis-7.0.4.tar.gz ] then echo "当前目录已经存在redis-7.0.4.tar.gz&q…...

Tinker 环境下数据表的用法
如果我们要自己手动创建一个模型文件,最简单的方式是通过 make:model 来创建。 php artisan make:model Article 删除模型文件 rm app/Models/Article.php 创建模型的同时顺便创建数据库迁移 php artisan make:model Article -m Eloquent 表命名约定 在该文件中&am…...

鸿蒙(HarmonyOS)项目方舟框架(ArkUI)之线性布局容器Row组件
鸿蒙(HarmonyOS)项目方舟框架(ArkUI)之线性布局容器Row组件 一、操作环境 操作系统: Windows 10 专业版、IDE:DevEco Studio 3.1、SDK:HarmonyOS 3.1 二、Row组件 沿水平方向布局容器。 子组件 可以包含子组件。 接口 Row(…...

JAVA——JDBC学习
视频连接:https://www.bilibili.com/video/BV1sK411B71e/?spm_id_from333.337.search-card.all.click&vd_source619f8ed6df662d99db4b3673d1d3ddcb 《视频讲解很详细!!推荐》 JDBC(Java DataBase Connectivity Java数据库连…...
Flask 用户信息编辑系统
Flask 用户信息编辑系统 web/templates/user/edit.html {% extends "common/layout_main.html" %} {% block content %} {% include "common/tab_user.html" %} <div class"row m-t user_edit_wrap"><div class"col-lg-12"…...

XML Group端口详解
在XML数据映射过程中,经常需要对数据进行分组聚合操作。例如,当处理包含多个物料明细的XML文件时,可能需要将相同物料号的明细归为一组,或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码,增加了开…...
rknn优化教程(二)
文章目录 1. 前述2. 三方库的封装2.1 xrepo中的库2.2 xrepo之外的库2.2.1 opencv2.2.2 rknnrt2.2.3 spdlog 3. rknn_engine库 1. 前述 OK,开始写第二篇的内容了。这篇博客主要能写一下: 如何给一些三方库按照xmake方式进行封装,供调用如何按…...

LeetCode - 394. 字符串解码
题目 394. 字符串解码 - 力扣(LeetCode) 思路 使用两个栈:一个存储重复次数,一个存储字符串 遍历输入字符串: 数字处理:遇到数字时,累积计算重复次数左括号处理:保存当前状态&a…...

深入理解JavaScript设计模式之单例模式
目录 什么是单例模式为什么需要单例模式常见应用场景包括 单例模式实现透明单例模式实现不透明单例模式用代理实现单例模式javaScript中的单例模式使用命名空间使用闭包封装私有变量 惰性单例通用的惰性单例 结语 什么是单例模式 单例模式(Singleton Pattern&#…...

【笔记】WSL 中 Rust 安装与测试完整记录
#工作记录 WSL 中 Rust 安装与测试完整记录 1. 运行环境 系统:Ubuntu 24.04 LTS (WSL2)架构:x86_64 (GNU/Linux)Rust 版本:rustc 1.87.0 (2025-05-09)Cargo 版本:cargo 1.87.0 (2025-05-06) 2. 安装 Rust 2.1 使用 Rust 官方安…...

Xela矩阵三轴触觉传感器的工作原理解析与应用场景
Xela矩阵三轴触觉传感器通过先进技术模拟人类触觉感知,帮助设备实现精确的力测量与位移监测。其核心功能基于磁性三维力测量与空间位移测量,能够捕捉多维触觉信息。该传感器的设计不仅提升了触觉感知的精度,还为机器人、医疗设备和制造业的智…...

【Linux】Linux安装并配置RabbitMQ
目录 1. 安装 Erlang 2. 安装 RabbitMQ 2.1.添加 RabbitMQ 仓库 2.2.安装 RabbitMQ 3.配置 3.1.启动和管理服务 4. 访问管理界面 5.安装问题 6.修改密码 7.修改端口 7.1.找到文件 7.2.修改文件 1. 安装 Erlang 由于 RabbitMQ 是用 Erlang 编写的,需要先安…...

热门Chrome扩展程序存在明文传输风险,用户隐私安全受威胁
赛门铁克威胁猎手团队最新报告披露,数款拥有数百万活跃用户的Chrome扩展程序正在通过未加密的HTTP连接静默泄露用户敏感数据,严重威胁用户隐私安全。 知名扩展程序存在明文传输风险 尽管宣称提供安全浏览、数据分析或便捷界面等功能,但SEMR…...
文件上传漏洞防御全攻略
要全面防范文件上传漏洞,需构建多层防御体系,结合技术验证、存储隔离与权限控制: 🔒 一、基础防护层 前端校验(仅辅助) 通过JavaScript限制文件后缀名(白名单)和大小,提…...
第22节 Node.js JXcore 打包
Node.js是一个开放源代码、跨平台的、用于服务器端和网络应用的运行环境。 JXcore是一个支持多线程的 Node.js 发行版本,基本不需要对你现有的代码做任何改动就可以直接线程安全地以多线程运行。 本文主要介绍JXcore的打包功能。 JXcore 安装 下载JXcore安装包&a…...