分布式相关知识入门学习(待续)

urlyy

本文是我在结合MIT6.824课程和网上诸多博客、课程的内容进行的总结
参考博客太多,就不写引用了

几个概念

  • 集群(部署方式):在多台不同的服务器中部署相同应用或服务模块,构成一个集群,通过负载均衡器对外提供服务。负载均衡可以分摊运行压力
    在这里插入图片描述

  • 分布式(部署方式):拆分一个大系统,将拆分出的多个子系统部署在不同服务器,各系统可通过网络通信进行协作
    常见的拆分方式:

    • 水平拆分:将项目按层次拆分,如拆分为数据层、业务层、界面层等
    • 垂直拆分:将项目按业务模块拆分,比如电商系统拆分为订单模块、用户模块、秒杀模块并分开部署

    关于微服务与集群的结合:

    • 分布式的每一个划分出来的业务模块都可以做集群,而因为分布式的特点,偏底层服务模块的垮掉会影响上层服务模块,所以为了保证系统的可用性会结合分布式与集群,先分布式再微服务(注意分布式中每一个结点都可以建立集群,但集群不一定是分布式)
  • SOA(面向服务的架构)(架构设计方式)
    相对于面向对象、层次结构这些设计方式,SOA将系统看做由多个服务组成,服务相互依赖、调用、协作最终提供功能

    • ESB
      说到SOA就不能忽略ESB(企业服务总线),他就像一个翻译官、信使等角色,将多个可能异构的服务串起来,形成一个大的系统,也是企业中集成各系统的一个常用手段。
      在这里插入图片描述
  • 微服务(架构设计方式):
    是SOA的进阶,因为微服务在将系统按服务划分的同时,它的“微”表现在尽可能微小化服务,小到一个服务只对应一个单一的功能,只做一件事,做到了模块的不可再拆分,类似于“原子性”。所以微服务算是分布式的垂直拆分的进一步拆分。但是微服务不一定要将应用分散在多个服务器上,只是更加强调单一职责和独立性。
    在这里插入图片描述

  • 主从架构
    这种架构把服务器分为了主机与从机,通过角色的分配进行了任务的分配,基于该架构有读写分离、双机互备之类的影响系统可靠性、可用性、性能的方案
    但是在实现主从机数据的同步以及主从切换上较为复杂,维护成本也相对较高

负载均衡

字面意思,把请求根据选定的算法,让下游接收到的请求是均匀分布的
可以按照软硬件、实现技术等来分类
软件中常见的就是Nginx和Haproxy吧

  • 负载均衡算法:

    • 静态(固定概率):轮询(基于取模,按编号大小依次分配)、加权、随机、加权随机、源地址hash、目标地址hash、一致性hash等
    • 动态(根据服务器实时情况):最小连接数、加权最小连接数、最小时延等
  • 在分布式系统中的负载均衡解决方案大概有以下几种:
    首先项目层次为:

    • 客户端层:比如用户浏览器、APP 端
    • 反向代理层:技术选型 Nignx 等
    • Web 层:前后端分离场景下, Web 端可以用 NodeJS 、Vue 、React
    • 业务服务层:用 Java 、Go,一般互联网公司,技术方案选型就是 SC 或者 Spring Boot + Dubbo 服务化
    • 数据存储层:DB 选型 MySQL ,Cache 选型 Redis ,搜索引擎选型 ES 等

    那么就有

    • 客户端层 -> 反向代理层 的负载均衡。通过 DNS 轮询

    • 反向代理层 -> Web 层 的负载均衡。通过 Nginx 的负载均衡模块

    • Web 层 -> 业务服务层 的负载均衡。通过服务治理框架的负载均衡模块,比如Dubbo

    • 业务服务层 -> 数据存储层 负载均衡。通过数据的水平分布,数据均匀了,理论上请求也会均匀。比如分库分表、通过买家ID分片等

一致性hash

hash算法在负载均衡中用的很多,因为它很简单,而且能保证同一个data能访问到同一个服务器
但是它不利于弹性伸缩
比如有三台服务器部署了redis,原先是使用hash(data)%3找到对应的服务器,但是突然网站被引流了,为了应付新增压力要增加服务器,那么负载均衡算法就变成了hash(data)%4,原先的数据会无法获取(比如4%3==2,4%4==0),甚至有可能导致缓存穿透,同时为了恢复正常业务就要想办法进行数据迁移,这个迁移要利用新的负载均衡公式,计算原先的服务器现在被分配到到哪个位置,然后再进行迁移,这是一个很麻烦的事情。
所以大牛想出一个法子,建立一个环,令环周长(也是取模数)为2 ^ 32,我就设mod_num=2 ^ 32吧。首先各服务器在环上的位置是通过取ip或者其他东西的hahs值再%mod_num。对于数据的存取,在获得key=hash(data)%mod_num后,根据key在圆环上的位置,顺时针遇到第一个服务器,把这个服务器作为这个数据绑定的存取服务器。
如果在下图的A->C这个圆弧插入一台服务器D,对于除了C->D的其他范围都没影响,而C->D这段的读数据请求会落到D服务器而不是原先的A服务器,但也算起到很大效果了
在这里插入图片描述
但是对于这种情况我们需要继续采取措施,因为可能会出现下图的情况,即服务器的位置很接近,此时大部分请求都会落在A服务器上,负载均衡效果不佳
在这里插入图片描述
对于这种情况大牛引入了虚拟节点
即在环上,对于每个服务器建立了多个索引,除了落在A位置表示A服务器外,落在A1、A2……都能表示访问A服务器,这样就能解决服务器分布不均的问题
在这里插入图片描述

分布式系统特性

  • 分布性。分布式把系统拆分了,各个部分部署在不同的服务器上,而各个部分可以通过网络通信进行协作,所以这些服务器在空间上也是可以随意分布的。
  • 对等性
    分布式系统中的各个计算机没有主从之分,组成分布式系统的所有节点都是对等的。
  • 并发性
    同一个分布式系统的多个结点可能会并发地操作一些共享的资源,如数据库、分布式缓存等
  • 自治性
    分布式系统中的各个结点都包含自己的处理机和内存,各自具有独立处理数据的功能。
  • 资源共享
    一旦授权,可以访问环境中的任何资源。

分布式系统优缺点

  • 优点:

    • 提升服务可用性 / 稳定性
    • 提升系统并发能力
    • 提升系统容错能力
    • 低延迟
  • 缺点:

    • 依赖网络,会因为网络问题导致系统数据丢失或不一致性;
    • 系统复杂化,系统监控维护,版本迭代发布变得相对复杂,成本高;
    • 一致性,可用性,分区容错性无法同时满足。

幂等性

幂等性是系统的接口对外一种承诺,保证一次和多次请求某一个资源应该具有同样的副作用。比如同一个用户因为网有点卡,一直看不到“支付成功”的提示,焦急中点了很多次“确认付款”按钮,系统应该保证只扣一次钱。
声明为幂等的接口会认为外部调用失败是常态, 并且失败之后必然会有重试。
常见实现方案:
1、对每个请求生成唯一ID,对同一ID的多个请求只执行最先到达的
2、对每个数据生成唯一ID,针对于插入有唯一标识属性的实体对象的场景,且可以利用唯一索引实现查重,比如利用订单号。
3、利用数据的状态字段,比如订单的状态已经在“已支付”了,就要拒绝执行“支付”的请求。
4、乐观锁,针对更新场景,比如设置一个version版本字段,初始时version为0,执行update后version改为1,接下来第二次update就会失败

1
2
3
4
5
# 使用了乐观锁的sql
# 注意没有使用status进行过滤,没有利用状态字段
UPDATE ORDER
SET status=#{status},version=version+1
WHERE id=#{id} AND version=#{version}

正式开始分布式具体理论和技术

CAP理论

在这里插入图片描述

  • C 一致性(Consisitency):客户端的每次读操作,不管访问哪个结点,要么读到的都是同一份最新的数据,要么读取失败。(强调数据一致性)
  • A 可用性(Availability):系统收到请求后一定能给出响应。(强调服务可用性)
  • P 分区容错性(Partition Tolerance):当节点间通信出现任意数量的消息丢失或高延迟的时候,系统可以继续提供服务。(下图)
  • 在这里插入图片描述

CAP三选二

CAP虽然都是好的特点,但实际上我们只能三选二,而且必须保证分区容错性
1、首先对于“必须保证分区容错性”。“分区”就是指能相互通信的一些节点服务器所组成的一个逻辑网络。因为多节点间的通信交互就是分布式的特点,而节点间的分区故障是不能保证绝不发生的,所以只要是分布式系统就必须保证分区容错性,否则会出现整个系统不能用的情况。
2、如果实现了一致性,如果节点间数据同步出现异常,基于一致性的定义,用户此时操作数据会返回操作失败,系统不满足可用性
3、如果实现了可用性,那么即使节点间数据同步出现异常,基于可用性的定义,还是得返回自己这个节点还未更新的旧数据给用户,系统不满足一致性。

但是

理论上,在不存在因网络问题导致一个大分区变成多个小分区的情况下,能保证CAP同时满足。
但是这个网络、服务器都是不可靠的,所以一般还是说CP或者AP,当数据一致性很影响系统或业务运行时,优先选择CP。

总之

CAP理论可以作为项目设计的指南针,我们可以从CAP三个维度出发,对项目需求进行分析,进而做出合理的选型,这对项目的设计、开发、维护、重构都是很有帮助的。

一致性模型

这是较完整的一致性模型图
在这里插入图片描述
但是我们一般还是谈及下面这些
在这里插入图片描述

  • 强一致性(线性一致性):从更新操作完成后的瞬间开始,任何后续读操作都能获得最新的值。也可以理解为在任意时刻,所有节点中的数据是一样的。开销很大而且很难实现。

  • 最终一致性
    在经过一段时间的同步后,所有结点中的数据才达到一致。尽管不同的进程读同一数据可能会读到不同的结果,但是最终所有的更新会被按时间顺序同步到所有节点。

  • 因果一致性
    要求有因果关系的操作顺序得到保证,非因果关系的操作顺序则无所谓。

  • 会话一致性
    在操作顺序得到保证的前提下,保证用户在同一个会话里读取数据时保证数据是最新的,如分布式系统Session一致性解决方案。

  • 单调读一致性
    用户读取某个数据值后,其后续操作不会读取到该数据更早版本的值。需要确保用户只能从有最新数据的节点读取数据。

  • 单调写一致性
    用户先写一次(w1),再写一次(w2),在所有节点看来,都是先执行w1再执行w2

  • 读你的写一致性(read your writes)
    保证用户总能马上看到自己提交的更新,但是不保证其他用户能马上看到这个更新。一个简单的方法就是对自己的数据只在主库读写,对别人的数据只在从库读写;其余的还有时间戳等方法。
    借鉴了这篇博客,感觉内容挺好

    分布式系统中的一致性模型

分布式事务ACID(酸😂)

ACID实现的是强一致性,根据CAP理论会损失可用性

两阶段提交协议(2PC)实现强一致性

结点分为两个角色,协调者和参与者
协调者作为中介,统筹控制相关参与者的数据一致化,并且协调者具有超时机制,而参与者没有
在这里插入图片描述
我自己画了个流程图,内容基本覆盖了2PC的过程,但是对于协调器出故障的情况只在二阶段给出了,而且异常结束也没给解决方案。
在这里插入图片描述
下图是别人博客里的一张好图,我就拿来用了

分布式事务:XA,2PC,3PC,TCC

在这里插入图片描述

第一阶段简单而言,就是协调者把事务内容分发,让各个参与者开始并锁住资源、执行事务(只剩下提交没完成)
第二阶段就是对第一阶段各参与者执行事务的情况进行处理,如果没全部执行成功就全部回滚事务(原子性),否则就全部提交事务。假如第二阶段失败,可采取的手段有:重试操作、记录日志后台定时任务补偿操作或通知人工补偿操作。

缺点
- 同步阻塞:在事务执行过程中,如果参与者占用了公共资源,会导致其他节点访问该公共资源的操作处于阻塞状态,降低了系统性能。
- 单点问题:若协调器出现问题会导致事务无法进行或事务一直处于阻塞。
- 数据不一致:当第二阶段是Commit请求时,如果因为局部网络异常或协调者中途崩溃导致只有部分参与者收到Commit请求,会导致收到了请求的和没收到请求的节点出现数据不一致的情况。
- 太过保守:任何一个结点的失败都会导致整个事务的失败。
- 无法解决的问题:如果协调者在发出Commit消息后宕机,而受到这条消息的参与者也宕机了,那么即使通过选举产生了新的协调者,这条事务的状态也是不确定的,没人知道事务是否已经被提交。

三阶段提交协议(3PC)实现强一致性

在2PC的基础上,给参与者也加上了超时机制,并将2PC的第一阶段分为两个阶段。第一阶段CanCommit只需要参与者查看自身情况,不需要开始事务占用资源;参与者在第三阶段接收超时则自行提交事务,因为前面两阶段已经走完,认为各个参与者都是会提交事务的,只是自己没收到DoCommit消息而已。
优点:通过给参与者超时机制,减少事务阻塞时间。
缺点:超时机制也导致可能出现数据不一致问题;阶段增加导致需要使用更多消息进行写上,增加系统负载和响应延迟;同时在实现上比2PC要复杂,所以在实际应用上不是很广泛。

(下面这张图画的简略了,有空我再重新补一下)
在这里插入图片描述
下面这张也是刚刚那个博客里的图
在这里插入图片描述

TCC(Try-Confirm-Cancel)实现强一致性

实际上,2PC和3PC都是针对数据库实现的,实现的是跨库事务的一致性。

在这里插入图片描述
然而,我们除了跨库操作要保持一致性之外,有些业务还要保证跨服务的一致性。
在这里插入图片描述
TCC分为Try、Confirm、Cancel三种操作,共两个阶段
Try对业务系统做检测,并冻结资源
Confirm对事务做提交,默认Try成功则Confirm一定成功
Cancel是当Try出错时,回滚业务,释放预留资源

在阶段2,调用方发生宕机,或者某个服务超时了,此时需要不断重试!不管是Confirm失败了,还是Cancel失败了,都不断重试。这就要求Confirm和Cancel都必须是幂等操作。注意,这里的重试是由TCC的框架来执行的,而不是让业务方自己去做。

TCC一般和2PC进行比较,因为他俩的过程其实差不多,但是TCC是业务层面上的分布式事务,而2PC是资源上的分布式事务。TCC的锁其实不算锁,更多的是封锁了对该字段的读或写操作(不可能一个人下订单导致其他人不能下订单吧),保证的是最终一致性,且锁的粒度更小,更灵活,减轻了数据库的压力,系统性能更好,但是实现起来较复杂,对代码的入侵性也很强,需要开发冻结、确认、撤销三套API进行操作,建议使用开源框架。而且在需要分布式事务能力时吗,优先考虑现成的事务型数据库(比如实现了XA的MySQL),当现有的事务型数据库不能满足业务需求时,在考虑基于TCC实现分布式事务。

BASE理论(碱😂)

Basically Available (基本可用)、Soft state (软状态)、Eventually consistent (最终一致性 ),核心是基本可用和最终一致性。软状态只是说明系统允许数据有一个过渡状态,即各节点间会有短暂的数据不一致现象。
Base理论是AP的扩展,是对互联网大规模分布式系统的实践总结,强调可用性。因为这类系统对可用性的需求更大,一些数据也不需要那么强的一致性。

基本可用

基本可用可以理解为系统出现故障时允许损失部分功能的可用,保证核心功能的可用,是一种妥协。常见方案有流量削峰(如果能控制数据,就可以做到比如错开几场秒杀活动的时间;如果不能控制数据,就将数据保存到队列)、延迟响应(请求都缓存进队列,减缓请求到业务层的速度,这也导致了响应时间的拉长)、体验降级(展示时使用缩略图等)、过载保护(存放请求的队列满了就拒绝请求进入)等等

最终一致

最终一致就是数据不会马上同步到所有节点,需要经过一段延迟才会达到数据一致的状态。举个例子,像今年情人节在QQ上零点发说说时,只能看到自己的说说,看不到别人的,但是上午就能看到其他人的说说了,这种措施影响不大,反正最后别人能看到你零点发了秀恩爱消息并且给你点赞就行了,但是提高了系统的可用性。
几乎所有的互联网系统采用的都是最终一致性。但是在数据敏感的业务上还是需要考虑事务和强一致性,比如支付系统和金融系统。

实现最终一致性的常用方式

  • 读时修复:写数据时不关注是否成功,只是在客户端想读数据时,先在多个节点间获取多个该数据,再根据一定的规则决定要返回的值,可以再以这个值修复刚才不是这个值的节点。(性能不好)
  • 写时修复:多个节点进行写入时,哪个节点写失败了,就在那个节点上把原本要写的数据缓存起来,系统不断通过重试进行修复。(不需要比较数据一致性,因为存在重试机制,默认其他节点都写入成功了,所以性能损耗比较低)
  • 异步修复:定时对账检测副本数据的一致性,不一致则修复。(性能不好)

一些实现最终一致性的具体方式(不全)

基于消息实现最终一致性

事务消息方案:RocketMQ
本地消息方案:首先消息队列如Kafka等,是用来实现跨服务通信的。
然后消息发送方(服务A,设为余额服务)的数据库需新增一张消息表,消息接收方(服务B,设为库存服务)的数据库需新增一张判重表(保证幂等性,因为是允许超时重试发消息这个操作的,会导致重复消息)。
此时支付开始,余额服务把修改余额记录和插入新消息记录放在同一个事务里,保证原子性;接着后台异步任务将消息表的新记录发给MQ;服务B作为消费者拉取得到消息,用这个消息里先在判重表内查重,重复则不执行,否则插入该消息并修改对应库存记录(注意判重、插入、修改也是在同一事务内的),执行完返回ACK给MQ,如果超时没发回ACK,MQ会再次推送,防止服务B宕机重启导致消息丢失的问题。

基于补偿实现最终一致性

saga模式(TODO)

分布式协议和算法

拜占庭将军问题

TODO

Paxos

TODO

Raft(只说明不增删节点的情况,因为还没学那么深)

参考了的文章

raft算法详解
极客时间 分布式协议与算法实战
MIT6.824 国人翻译课件
Raft协议详解
Raft梳理

推荐两个网站
Raft动画讲解
可交互的Raft

Raft是工程上使用较为广泛的强一致性、去中心化、高可用的分布式协议。遵从此协议的分布式集群会对某个事情达成一致的看法,即使是在部分节点故障、网络延时、网络分割的情况下。

角色定义

Raft算法中定义了三种角色,即

  • leader(领导者):负责与客户端的通信,接收到客户端的命令后将其转发给follower,管理日志复制,不断发送心跳给follower。
  • follower(追随者):执行leader传来的命令
  • candidate(候选者):是一个中间状态,当follower长时间没收到 leader的消息就会成为candidate,开始一次选举,抢夺成为leader的资格,抢夺成功的candidate成为leader,抢夺失败的重新变成follower

选举

Raft作为一个“一切以领导者为准”的算法,领导者的选举是一件大事。同一时间领导者只能存在一个是必须实现的机制。下面我由浅到深的举例子,尽量说清楚要做出哪些努力。

初始情况

此时服务器集群刚刚启动,所有结点都是follower,任期号全是0。我们先要推选出一名leader。首先提出一个机制:当超时时间结束时,follower会变为candidate,并将自己的任期+1,并开始使用自己的选票数属性,先给自己投一票,然后发请求给其他节点,邀请他们为自己投一票;受到邀请的节点重置自己的超时时间,然后因为发来的任期是1,比自己的任期0要大,所以更新自己的任期为1,并给A投出一票(返回一个投票消息给A)。

如下图,节点A最先超时,那么节点A在150ms后变为candidate,任期编号+1后变为1,邀请其他节点为自己投票。此时还未超时的节点B和C收到了来自A的投票邀请,更新自己的任期为1,重置自己的超时时间,给A投出一票,同时。在选举超时时间为0之前(选举也是有超时时间的),因为选票数>节点数的一半(每个节点都会记录总节点数的),所以节点A会成为领导者。
在这里插入图片描述
先别急着看leader上任后的操作,先想想如果节点A和节点B的超时时间相同,它们同时超时并发起选举,会发生什么事?
我再告诉你选举中的一个机制:candidate邀请其他节点投票时,会把自己+1后的那个任期编号发过去,其他节点收到这个任期编号时,如果这个任期比自己的任期要大,说明自己应该推举他,同时更新自己的任期编号为这个投票邀请的任期;如果其他节点在投票邀请发来之前变成了candidate,他的任期+1了,那等到投票邀请发来,发现和自己任期一样,那肯定不给竞争对手投票,所以不投;如果某个follower已经给某个candidate投票了,那么它任期也更新了,其他的相同任期的投票邀请过来就直接拒绝掉了,不会重复投票。**(总结就是如果发来的消息的任期比自己的大,就更新自己的任期;而且其实如果发来的消息的任期比自己任期要小的话,会直接拒绝这个消息)** 很明显这个机制对于上面那张图的节点个数情况,如果我还假设A、B同时超时,即两个candidate、一个follower的情况,是能防止出现票数相同的情况的,因为如果A和B同时超时,C也只能投给A或B,不能都投。
所以我再给个例子,如下图。对于这种偶数个节点,是完全可能出现选票相同的情况的。对于这种情况,Raft是会进行重新选举的。但应该有更明智的方案去减少这种情况的发生,而不是每次都进行重新选举这种降低可用性的兜底策略。
所以对于每个节点的超时时间,在Raft中是随机生成的,这种方法很大程度上减少了多个candidate同时超时的情况。那么先超时的就能当上follower了。即使运气不好随机到多个结点同时超时,那我就重新选举嘛。
在这里插入图片描述
好了,选举还未超时,节点A发现自己票数比总节点数的一半还大,于是自封为“leader”,但还得告知其他节点,不然他们又要开始选举了。所以节点A周期性发送心跳,告诉结点B和C“节点A是领导者,任期是1”。节点B和C收到心跳,重置自己的超时时间,继续作为follower。领导者开始接收客户端发来的命令,并在心跳消息中将新的日志(后面再讲)连带发给follower。
在这里插入图片描述

异常情况(暂不考虑日志) - follower宕机

follower作为一个被动的角色,他的宕机不会对集群的运作有太大的影响。
但是当他重启恢复后,他要跟上集群的步伐啊,别人都任期是5了,他还在2,那真是”不知有汉,无论魏晋”了。所以不论发来的是leader的心跳还是candidate的投票邀请,他都会用请求里附带的任期更新自己的任期。(其实和前面讲的更新任期的策略是一致的)

异常情况(暂不考虑日志) - leader或candidate宕机

leader挂了,其他节点重新选举新leader呗;candidate挂了不用重新选举。不过当它恢复过来后,收到发来的消息后发现自己任期居然比其他人还低,那它就乖乖变成follower了,并用发来的消息更新自己的任期。

异常情况(暂不考虑日志) - follower/candidate一直收不到leader的 心跳

该follower因为网络故障等问题一直接收不到心跳,这种情况follower先转化为candidate,然后,如果不进行特殊处理,它会一直增加自己的任期,当网络恢复后,这个candidate可能比leader的任期还大,直接把一个正常的leader逼回follower,这会影响集群的稳定性。所以在工程实现上会在candidate请求其他节点投票之前,增加一个preVote预投票阶段,在这个阶段,candidate不增加自身term,只广播投票请求,只有拿到多数投票才进入正式请求投票阶段

日志

日志复制操作的时序

在这里插入图片描述
我这里给个场景,假设设这个Raft的上层是一个KV数据库,此时外部应用程序访问数据库应用程序 要PUT
1、leader接收该PUT操作
2、leader发送一个添加日志(AppendEntries)的RPC给其他follower,然后写入一个状态为uncommited的PUT操作的日志,等待follower们的响应
3、follower收到日志后,也在本地写入一个uncommited的该日志,返回给leader响应。
4、leader收到过半节点的正确响应后,会执行这个请求,即提交给应用程序,让数据库真正执行PUT操作,并且将得到的结果返回给客户端,同时把这个日志改为commited。然后因为follower还要知道leader已经commit了,自己才能commit,所以leader还要再下次心跳中附加一个信息,通知follower们从这个日志里取出操作让他们的上层数据库执行,把他们的这条日志状态改为commit。

日志的作用

从日志复制操作可以发现,首先日志是不断append的,所以日志表示了leader选择的顺序。
同时,因为节点收到日志和实际执行操作是有时间差的,日志相当于这个操作的临时载体。
而且,如果leader发送给follower的操作因为网络原因在中途丢失,leader可以重传这个日志。
还有,它可以帮助重启的服务器恢复状态,即作为持久化的手段。

实现日志一致

TODO

其他的异常情况

leader性能远超follower(选自MIT6.824)

follower在执行leader传来的操作前会确认并将操作堆积在日志中,如果一直这么下去,follower的执行将无限落后于日志的堆积,最后耗尽内存。Raft没有处理这种情况。
所以在实际系统中需要额外增加一种消息,即由follower发给leader,告知它follower们执行到哪一步了,并以此控制leader的速度不至于太快。

脑裂

为什么我在那个大标题“Raft”上要加个”只说明不删减节点”,因为网上流传的知识,大部分都不涉及这部分知识,6.824课程对这块也只是提到“Raft的作者提出了方法来处理这种场景,但是比较复杂。”
而且都说了是分布式协议这种理论知识了,实践什么的看后续的改进吧。
(有点问题,先别看)
那么说脑裂这个事。什么是脑裂?
举例子,一个集群ABC,客户端发“SET a=1234”的请求给A,A就同步给集群其他人,之后客户端们不管读集群里谁的数据,都能获得”a=1234”。
但是如果A和BC断开通信了,那么A一个分区,BC一个分区,客户端先对A说”SET a=1234”,A无法将该写请求同步到BC上;这时又来一个客户端对B说”SET a=5678”,这个写请求也只能同步到C上,到不了A那。那么这个集群里就出现了数据不一致现象了。
对于此,如果Raft不采用动态扩容的话,因为“投票必须过半”这个机制,我们可以事先只部署奇数个节点,那么总有一方节点数少于一半,一方节点数多于一半。如果原先的leader处于多于一半的那方,客户端继续与这个leader通信,而少于一半的那方不与leader通信,相当于不与客户端通信,对外界而言此时的集群只有leader这边的分区,数据一致性继续保持;如果原先的leader不在多于一半的那方,那么

一些杂的话

有些知识点找学长问了下,确实为了保持内容贴合Raft的理论知识,我加了很多限制条件,但实际分布式项目中哪来这么多限制条件,总要在理论上进行改造、扩展甚至违背理论,就比如反范式这种。所以理论可以了解,但是不能奉为教条。

Gossip

TODO

Zab

TODO

分布式缓存

TODO

分布式数据库

TODO

分布式计算

TODO

分布式文件系统

TODO

  • 标题: 分布式相关知识入门学习(待续)
  • 作者: urlyy
  • 创建于 : 2022-11-15 22:46:41
  • 更新于 : 2023-07-03 03:17:50
  • 链接: https://urlyy.github.io/2022/11/15/分布式相关知识/
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。