NET Core微服务之路:再谈分布式系统中一致性问题分析

前言

一致性:很多时候表现在IT系统中,通常在分布式系统中,必须(或最终)为多个节点的数据保持一致。世间万物,也有存在相同的特征或相似,比如儿时的双胞胎,一批工厂流水线的产品,当然,我们不去讨论非IT以外的知识点。

注:我们一定要明白一个词叫“信息不对称”,不论是人、事、物,信息不对称是永远都存在的,要知道,在IT系统中,能引起信息不对称的因素有很多,比如网络上,有丢包、有延迟。硬件上,有不同性能的计算能力和处理能力。

在传统的IT时代,一致性通常是指强一致性,比如一个单体的WEB程序中,从数据库到缓存,再到呈现出来的界面,数据均是相同的;

而在现在的互联网时代,特别是分布式架构下,一致性的含义远远超出她原有的含义,由于互联网的特点,信息量巨大,每个人(或者不同的每个业务)获取到的信息不对称,最终都会造成不一致。

换句话说,传统的单体应用无法满足巨大的信息量,而转向为多节点、多服务的分布式架构模型(正如CPU,从单核变为多核来支撑更多的计算能力),多个服务多节点必然会产生数据不一致的问题,正如虽然人多力量大,可如果每个人都往不同方向使劲,这个力量就是分散的,不一致的,没有任何作用的,所以,我们需要管理,需要告诉每个人使劲的方向,告诉每个人的排列顺序等等一系列有效的分配和管理,于是乎,我们将这个话题转化为互联网思维就是“拆”(非拆迁办)。

我们很多时候都在讨论这个程序如何拆分,比如拆成业务层、数据层、提供层、UI层、缓存层等等,而每一层的部署大部分情况下都不会存在于一个相同的宿主中(否者怎么还叫拆),这样称为水平拆分(或横向拆分),将这些不同的层部署成多个节点,多个节点如果具有相同的一致性功能,那么再组成一个服务域,这样,把这个服务域作为一个处理中心,在处理能力和计算能力上,远远会超过单体程序的整体性能。

这样的优势显而易见,但是,最大的问题(不能说是缺点)是,由于拆分后的系统或者服务化的系统,存在多个元功能模块,或者一个服务域中的多个节点,如何去保证她们数据的一致呢?

640?wx_fmt=jpeg

提出问题

直接用A,B,C等等常规化表述形式去描述不一致问题,网上文章很多,我们尝试换一种方式,以实际的、你我都会接触到的现实场景,来理解不同情况下所产生的不一致问题。

Q1.生活案例:意愿

假如,别假如了,就拿笔者来说吧。当初跟妻子去买婚戒的时候,只想买个一般的对戒便可,因为日后的日子里用钱的地方太多(相信任何一个已婚男士都有这种体会),可妻子喜欢那种布灵布灵的,要知道,这种布灵布灵的,可比一般的要贵出许多许多,那么,这个不一致就开始形成了,你的意愿和她的意愿并没保持一致,日后生活问题会越来越大。当然,这只是一个几年前的例子,显然我妻子已经接受了我当初的观点。

Q2.银行案例:转账

转账是不一致案例中最经典的例子。这么来说,假如你想通过某个平台,比如支付宝、微信、银行进行转账,这个平台的流程是这样的:首先减去你账户上的余额,然后加到你指定账户上去。如果平台减去你的账户余额成功了,而增加其他账户的的余额失败了,那么你将损失这笔钱;如果减去你的账户余额失败,而增加其他账户的余额成功,那么银行将损失这笔钱;这种资金上的不一致是绝不允许存在,否则这个平台将面临破产和倒闭。

Q3.常见案例:下单和扣库存

不管是电商还是企业仓库,都会存在一个经典案例。比如,你在某个界面上进行了下单,比如她显示的库存是100件,而你订购了1件,并支付了应该的费用,可是,这个库存数量并未任何变化,那么,如果有101个人前来购买,那么会出现超卖的情况,也就是这多出来的1个人将买不到这件商品,这就是下单和库存不一致所导致的。反之,永远下单不成功,而库存却在减少,这对企业来说都是增加运营成本的。

Q4.常见案例:掉单

掉单一般常见于协作工作的系统的流程中,分别对彼此的上游和下游。比如上面的案例,你已成功下单并支付,按照流程,平台应该告诉物流我有物品需要配送,过来取单,可物流终究收不到这个收单请求,导致物品配送失败,严重会导致赔付,这样的不一致性通常是一个系统中的两个请求不一致而造成的。

Q5.系统案例:系统状态

这个案例跟上面的掉单案例类似,只是需要区分引起这个掉单的原因,其实就是两个系统的状态不一致所造成。比如上游及时收到请求并响应,而下游却因为某个状态(原因)而没响应这个请求,最终导致不一致形成。

Q6.系统案例:本地缓存和数据库

现在存储都依赖于数据库,而关系型数据库都具备ACID特性,但是在大规模高并发的互联网系统里,一些特殊的场景对读的性能要求极高,为止,服务在这个上面的数据库将难以抗住大规模的读流量,为了应对这样的问题,一般都会在数据库前增加缓存,那么缓存和数据库之间的数据是如何保持一致?是需要强一致还是若一致?

Q7.系统案例:节点缓存

一个服务域上的多个节点为了满足较高的性能要求,需要使用到本地缓存,使用了本地缓存,每个节点都会有一份缓存数据的拷贝,如果这些数据是静态的、不变的,那永远都不会有任何问题。但如果这些数据是动态的、经常更新的。那么问题就来了,当被更新的时候,各个节点的更新都会存在先后顺序,而正是这先后顺序的一瞬间,各个节点的数据将会不一致。想象一个高流量读的场景中,一个请求拿到的数据是1,而另一个请求拿到的是0,这将导致灾难性的后果。

Q8.系统案例:超时

服务化的系统间调用常常因为网络问题导致系统间调用超时,这是在即便在网络最好的机房下、在上亿次的前提下,同步调用超时也是必然会存在的,正如上面提到的不同状态类似,假如当下单和物流存在极高请求的情况下,物流并未及时反馈响应,而下单却并不知道物流是否已经接到订单,或者定已收到订单,只是掉包了等现象,这样的不一致,也是会导致灾难性后果的。

解决模型

对于Q1问题,我们可以有两套方案,一是不结了,不过显然这是行不通的;二是慢慢的补偿,先买个一般的,等手头资金充裕了,或者某天中了500万,或者天上掉个金块,再给她个惊喜,于是,这个问题解决了,大家的意愿都一致了,都开心了。可见,这样的解决模式会存在一个现象---过渡时期,当这个过渡时期到最终双方都达成一致时,问题就解决了。因此,我们不能要求在买对戒的时候,双方都达到强一致的要求,生活是必须要过下去的,不可能说散就散,那么我们要考虑去补偿她,尽最大的努力达到最终一致。

在化学单词中,“ACID”是酸,我想这绝对不是巧合,原文可参考百科

640?wx_fmt=png

(image form Wilfred Springer,)

因此,数据库会从一个明确的状态到另外一个明确的状态,中间的临时状态是不会出现的,EF的追踪功能也正是遵循这个原则进行设计,任何一个操作状态在中途是不会存在改变,如果出现改变,将会给你提示错误,当然,你可以关掉它,不过这样EF的核心点就不存在了。

  • A: Atomicity,原子性

  • C: Consistency,一致性

  • I: Isolation,隔离性

  • D: Durability,持久性

回到Q2和Q3的问题上,使用关系型数据库可以解决这样的强一致性需求,然而,单纯通过强一致性的数据库去面对不断拆分的元组,是难以满足互联网高流量的需求的,或许你会说使用服务器固态硬盘和读写分离的模式去应对,但这绝对只是一个应对方案而已。因此,在拆分的时候尽量把转账的相关账户放入一个数据库分片,而Q3上,把订单和存库放入一个分片,因为中途不会存在任何改变,从0到100必须保证任何状态都是原始的初始状态。

但是,我们就Q2假设另外一个场景,假设账户的数量巨大,对账户存储进行了拆分,关系型数据库分为8个实例,每个实例8个库,每个库8张表,共512张表,假如转账的账户正好在一个库里,这个问题依赖关系型数据库的事务来保持强一致性,但是,如果两个账户在不同的库里,这个事务就无法封装在同一个数据库中的,这样就会发生一个账户扣款成功,而另外一个库的账户增加失败的情况。

帽子理论证明,任何分布式系统同时只可满足两点,没法三者兼顾。

  • C:Consistency,一致性, 数据一致更新,所有数据变动都是同步的

  • A:Availability,可用性, 好的响应性能,完全的可用性指的是在任何故障模型下,服务都会在有限的时间处理响应

  • P:Partition tolerance,分区容错性,可靠性

关系型数据库由于关系型数据库是单节点的,因此,不具有分区容错性,但是具有一致性和可用性,而分布式的服务化系统都需要满足分区容错性,那么我们必须在一致性和可用性中进行权衡,具体表现在服务化系统处理的异常请求在某一个时间段内可能是不完全的,但是经过自动的或者手工的补偿后,达到了最终的一致性。 

640?wx_fmt=png

而BASE理论的提出,解决了CAP在分布式系统中的一致性和可用性不可兼得的问题。“BASE”在化学单词中是指碱,因此我们可以想到一个词语叫“酸碱平衡”,而在实际的场景中,我们可以分别使用ACID和BASE来解决分布式服务化系统的一致性问题。BASE理论与ACID理论完全不同,她满足CAP理论,通过牺牲强一致性而获得可用性,一般应用在服务化系统的应用层,通过达到最终一致性来尽量满足不同业务上的需求。

  • BA:Basically Available,基本可用

  • S:Soft State,软状态,状态可以有一段时间不同步

  • E:Eventually Consistent,最终一致,最终数据是一致的就可以了,而不是时时保持强一致

640?wx_fmt=png

按照BASE模型实现的系统,由于不保证强一致性,系统在处理请求的过程中,可以存在短暂的不一致状态。系统在做每一步操作的时候,通过记录每一个临时状态,在系统出现故障的时候,可以从这些临时状态中继续完成未完成的请求处理,或者回退到原始状态,最后达到一致的状态。

例如Q1的转账状态为例,我们把两个账户的转账情况粗分为四个状态:

  • 第一个状态为准备状态,用户准备向另外一个进行用户转账;

  • 第二个状态为扣额状态,系统将从转账用户中扣去相应余额;

  • 第三个状态为加额状态,系统将从收款用户中增加相应余额;

  • 第四个状态为完成状态,系统转账完成后的确认;

在这过程中,系统需要将每一步的操作状态都进行记录,一旦某个环节出现故障,系统能够发现故障环节并继续完成未完成的任务,最终完成任务,达到一致的最终状态。在实际的业务生产环境中,通常每个阶段的状态都是通过持久化的执行任务,一旦出现了问题,定时任务会检查未完成的任务,继续执行未完成的任务,直到执行完成为止;再或者,该状态是属于取消状态,跟数据库事务执行方式一样,那么这个过程中已经完成的状态应该回滚到原始状态,整个过程还可以细化到如下更加详细实际转账流程:

640?wx_fmt=png

不过,由于这种方法在每个状态执行的时候都需要记录下来,而且需要更新数据库中的状态信息,一旦在大规模高并发下,性能将会是一个严重的瓶颈。

总结

  • 如果钱不是问题,那么最最简单的方式是使用向上扩展,利用强悍的硬件性能来运行专业的关系数据库,能否保证强一致性,比如Orcele和DB2这样符合工业标准的数据库。

  • 如果钱是个问题,那么相关的数据分到数据库的同一个分片,能够保证使用关系型数据库实现强一致性,比如Mysql。

  • 如果是业务限制,无法将相关的数据分到同一个片,就需要实现最终一致性,通过记录事务的状态来判断,一旦处理不一致,可通过自动化(如定时)或者人工干预来继续执行,并修复不一致的情况。

下一篇我们再多介绍一下一致性的更多具体实现方案。

参考

《浅析分布式一致性模型》http://loopjump.com/distributed_consistency_model/


原文地址:https://www.cnblogs.com/SteveLee/p/About_The_Distributed_Consistency.html

.NET社区新闻,深度好文,欢迎访问公众号文章汇总 http://www.csharpkit.com
640?wx_fmt=jpeg

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

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

相关文章

使用 Nexus3镜像搭设私有仓库(Bower 、Docker、Maven、npm、NuGet、Yum、PyPI)

Docker - 使用 Nexus3 搭设私有 NuGet 仓库Nexus 默认帐号Repositories上传组件包(Package)Repositories 说明准备 Package上传 Package使用 Package拉取 Nexus 镜像运行 NexusNuGetNexus 私有仓库前言说明安装Nexus NuGet 仓库简单使用总结前言NuGetNuget 是免费、开源的包管理…

P3293 [SCOI2016]美味

P3293 [SCOI2016]美味 题意: 有n个数组a,现在有m个询问,每次给出一个b和x,问b xor (a[i] x)的最大值是多少? 题解: 不难看出01Trie的题目 我们设ansa[i]x,我们想要b xor ans的最大值,这个很…

EF Core 小坑:DbContextPool 会引起数据库连接池连接耗尽

DbContextPool 是 ASP.NET Core 2.1 引入的新特性,可以节省创建 DbContext 实例的开销,但没有想到其中藏着一个小坑。最近有一个 ASP.NET Core 项目持续运行一段时间后日志中就会出现数据库连接池达到最大连接数限制的错误:System.InvalidOpe…

Kubernetes架构为什么是这样的?

小编序:在上周发布的《从“鸿沟理论”看云原生,哪些技术能够跨越鸿沟?》一文中,灵雀云CTO陈恺表示:Kubernetes在云计算领域已经成为既定标准,进入主流市场,最新版本主要关注在稳定性、可扩展性方…

开源 , KoobooJson一款高性能且轻量的JSON框架

在C#领域,有很多成熟的开源JSON框架,其中最著名且使用最多的是 Newtonsoft.Json ,然而因为版本迭代,其代码要兼容从net2.0到现在的最新的net框架,并且要支持.net平台下的其它语言,所以最新发布版本的Newtonsoft.Json其dll大小接近700k,另一方面,因为其复…

分布式系统的构建原则

什么是构建一个可维护和可扩展的系统的意义?在早期,一个系统的形态,只是满足用户和服务器资源之间的通道,唯一要扩展和维护的是系统后面的资源,保证资源的可用和够用,而系统本身的压力并不大。系统设计跟我…

可持久化4--可持久化并查集

可持久化并查集 可持久化并查集 按秩合并并查集 可持久化数组 首先并查集不能采用路径压缩,这是因为一次findR操作中,fa数组的很多位置(u->ru)会发生修改,由于每次修改都需要在可持久化数组上复制产生log个新结…

ASP.NET Core 2.2中的Endpoint路由

在ASP.NET Core 2.2中,新增了一种路由,叫做Endpoint(终结点)路由。本文将以往的路由系统称为传统路由。本文通过源码的方式介绍传统路由和Endpoint路由部分核心功能和实现方法,具体功能上的差异见官方文档。在升级到AS…

AtCoder2063 [AGC005E] Sugigma The Showdown(博弈论)

problem 洛谷链接 solution 考虑一条 (u,v)(u,v)(u,v) 的红边,在蓝树上 u,vu,vu,v 两点距离 ≥3\ge 3≥3。 如果先手到达 u,vu,vu,v 其中任何一点且下一步后手行动无法抓住先手,那么这个游戏就将进入死循环了。 通过画图,你会发现这个结…

基于ASP.NET Core的模块化设计: 虚拟文件系统

土牛亲自录制的本文介绍视频Abp中文网(https://cn.abp.io/)提供翻译字幕基于ASP.NET Core的模块化设计: 虚拟文件系统简介创建模块化的应用程序很困难. 构建模块化的用户界面更加困难. 需要单独开发模块的页面和组件,但是最后要把它们集成在一起像单个UI一样创建这样的模块化架…

[学习笔记] 乱世之神杀疯了 —— K-D tree

文章目录K-D tree建树合并插入删除查询(估价函数)旋转坐标系题目练习[SDOI2012]最近最远点对[Violet]天使玩偶/SJY摆棋子[CQOI2016]K远点对[国家集训队]JZPFARThe closest M points简单题巧克力王国[BOI2007]Mokia 摩基亚[CH弱省胡策R2]TATT[BZOJ3815]卡常数[NOI2019]弹跳A sim…

【春华秋实】.NET Core之只是多看了你一眼

技术学习是一件系统性的事情,如果拒绝学习,那么自己就会落后以至于被替代。.NET也是一样,当开源、跨平台成为主流的时候,如果再故步自封,等待.NET的就是死路一条,幸好.NET Core问世了,社区反响积…

[SDOI2010]粟粟的书架

[SDOI2010]粟粟的书架 题意: 一个R * C的矩阵,每个位置都有个数page[ij],现在选定一个小矩阵范围(给左上角坐标,和右下角坐标),问这个范围内的数总和是否大于h,如果大于h的话最少选几个数aij 对于50%的数…

基于Asp.Net Core的简单社区项目源代码开源

2019年3月27号 更新版本 本项目基于 ASP.NET CORE 3.0EF CORE 3.0开发使用vs2019 sqlserver 2017(数据库脚本最低支持sql server 2012/)使用步骤:1.下载相关开发工具2.运行数据库脚本目录下的相关脚本3.默认前端账号密码: 18812345678 1234564.默认后台账号密码: admin 123456开…

[学习笔记] 如果你愿意学那么你是可以看的懂的 —— 群论与 burnside 引理和 polya 定理

群与子群 <G,op><G,op><G,op> 是一个群需要满足以下条件&#xff1a; opopop 是一个满足结合律的二元运算&#xff0c;如 *&#xff0c;。GGG 是一个集合&#xff0c;存在单位元 eee。GGG 中所有元素都有逆元。即 GGG 对 opopop 运算封闭&#xff0c;封闭简单…

为什么从前那些.NET开发者都不写单元测试呢?

楔子四年前我虽然也写了很多年代码&#xff0c;由于公司虽然规模不小&#xff0c;却并非一家规范化的软件公司&#xff0c;因此在项目中严格意义上来说并没有架构设计、也不写单元测试&#xff0c;后来有幸加入了一家公司&#xff0c;这家公司虽然也是一家小公司&#xff0c;但…

使用 xUnit 编写 ASP.NET Core 单元测试

还记得 .NET Framework 的 ASP.NET WebForm 吗&#xff1f;那个年代如果要在 Web 层做单元测试简直就是灾难啊。.NET Core 吸取教训&#xff0c;在设计上考虑到了可测试性&#xff0c;就连 ASP.NET Core 这种 Web 或 API 应用要做单元测试也是很方便的。其中面向接口和依赖注入…

记录使用 Cake 进行构建并制作 nuget 包

前段时间折腾了一下&#xff0c;总算是把我自己的图片缓存控件&#xff08;https://github.com/h82258652/HN.Controls.ImageEx&#xff09;发布到了 nuget 上&#xff0c;目前已经进入一个比较稳定的版本了&#xff0c;基本没有很严重的 bug 了。其实核心代码早就写完了&#…

DDD领域驱动设计理论篇 - 学习笔记

一、Why DDD?在加入X公司后&#xff0c;开始了ASP.NET CoreDockerLinux的技术实践&#xff0c;也开始了微服务架构的实践。在微服务的学习中&#xff0c;有一本微软官方出品的《.NET微服务&#xff1a;容器化.NET应用架构指南》是我们学习的葵花宝典&#xff0c;纵观微软官方放…

.NetCore使用skywalking实现实时性能监控

一、简介很久之前写了一篇 《.Net Core 2.0 InfluxDBGrafanaApp Metrics 实现跨平台的实时性能监控》关于NetCore性能监控的文章&#xff0c;使用InfluxdbAppMetrics进行项目性能监控&#xff0c;由于技术有限&#xff0c;在正式环境使用一段时间后&#xff0c;莫名的AppMetric…