浅谈分布式一致性协议|青训营笔记

106 阅读8分钟

这是我参与「第四届青训营 」笔记创作活动的第7天

一.分布式系统

1.挑战

  • 数据规模越来越大
  • 服务的可用性要求越来越高
  • 快速迭代的业务要求系统足够易用

2.理想分布式系统

  • 高性能:可拓展、低时延、高吞吐
  • 正确:一致性、易于理解
  • 可靠:容错、高可用

3.最简单KV

Client提供RPC发起请求,将数据存储在DBEngine再返回。 image.png

  • 无容错
  • 无高可用
  • 正确性:单进程,所有操作顺序进行

二.一致性共识算法

1.复制过程

  • 主副本把所有的操作打包成Log(也就是operation,所有的Log写入都是持久化的,保存至磁盘上)
  • 应用包装成状态机,只接收Log作为Input
  • 主副本确认Log已经成功写入到副本机器上,当状态机apply后,返回客户端。

image.png

2.读操作

  • 直接读状态机,要求写操作进入状态机后再返回Client
  • 写操作复制完成后直接返回,读操作Block等待所有pending log进入状态机
  • 可能存在刚刚写入的值读不到的情况(在Log中)

3.KV常见的一致性模型

  • 线性一致性:写了马上就能读到
  • 最终一致性:读取可能暂时读不到但是总会读到

4.复制协议:当失效发生

主副本失效

  • 手动切换到从副本,只要速度够快,还能保证可用性 保证主副本失效:
  • 在切换过程中,主副本又开始接收client端的请求
  • 两个主副本显然不正确,log会被覆盖写掉
  • 我们希望算法能在这种场景下仍保持正确

5.共识算法:对于一个值,所有人都认同

  • 错误总是发生,类型很多,容错很难完成
  • 共识协议不等于一致性
  • 应用层面不同的一致性,都可以用共识协议来实现:比如可以故意返回旧的值
  • 简单的复制协议也可以提供线性一致性
  • 一般讨论共识协议时提到的一致性,都指线性一致性,因为弱一致性往往可以使用相对简单的复制算法实现

三.从Raft入手

1.Raft

  • 易于理解作为算法的设计目标
  • 使用了RSM、Log、PRC的概念
  • 直接使用RPC对算法进行了描述
  • Strong Leader-based
  • 使用了随机的方法减少约束
  • 正确性:形式化验证、拥有大量成熟系统

2.复制状态机

RSM:Raft中所有的consensus都是直接使用Log作为载体 Client把一个log提供给Raft,状态机确定log commit后,就提供给用户状态机,log写入后就返回给Client。

  • 一旦Raft更新Commited Index,意味着这个Index前的所有Log都达成共识了,可以提交给状态机了
  • Commited Index是不持久化的,状态机也是volatile,不稳定的,重启后从第一条Log开始
  • 状态机可能是不一样的,能读取的log也是不一样的,commit也可能不一样,但是会保证最终的状态一样

image.png

3.角色及Raft整体流程

  • Leader:所有操作的发起者,把log同步给follower,确定哪个log已经commit
  • 当没有leader时,follower会尝试成为leader,成为candidate,让大家投票给它,成为leader

image.png

4.Raft Trem

  • 每个Leader服务于一个term
  • 每个term至多只有一个leader
  • 每个节点存储当前的term
  • 每个节点term从一开始,只增不减
  • 所有rpc的request.reponse都携带term
  • 只commit本term内的log

5.Raft主节点失效

  • Leader定期的发送AppendEntries RPCs给其余所有节点
  • 如果Follower有一段时间没有收到Leader的AppendEntries,则转换身份成为Candidate
  • Candidate自增自己的term,同时使用RequestVote PRCs向剩余节点请求投票
  • raft在检查是否可以投票时,会检查log是否outdated,至少不比本身旧才会投给对应Candidate
  • 如果多数节点投给它,则成为该term的leader

6.Raft安全性-同Term

对于Term内的安全性:
对于所有已经commit的<term,index>位置上至多只有一条log 由于Raft的多数派选举,可以保证在一个term中只有一个leader

7.Raft安全性-跨Term

对于跨Term的安全性:
如果一个log被标记commited,那这个log一定会在未来所有的leader中出现 证明:

  • Raft选举时会检查Log的是否outdated,只有最新的才能当选leader
  • 选举需要多数派投票,而commited log也在多数派中(必有overlap)
  • 新leader一定持用commited log,且leader永远不会overwrite log

8.Raft Leader Failure

image.png

1代表这个log中的term,一号二号三号log,每个log都会带上term,每个leader收到一个log的时候,会标记上自己的标号,然后把它分发给剩余的follower。
对于s1的问题:假如现在123,1是leader,他提交了X,然后他复制给S2和S3,在它接受了一个新的log W,还没来得及给别人复制,他就出于某种情况进程卡死或网线被断开了,就不是leader了。
s2问题:一次同步了两条log,S2确定TQ已经commit了,commit index往前走了,但是s3还没有获得下一次log的通知,index就仍在原地。
s3发现没有leader后,他就自己的term加一,然后向S1和S2都请求request vote。s1的时候发现request是来自s3的,然后自己立刻会把term也提升到3,与此同时答应他的request.因为s3是来自更新的时期的term,所以他把自己更新的同时也同意他的投票。
s3成为leader后会尝试把自己的log同步给S1。这里可以发现,index等于2时,这个log冲突了,leader不会考虑follower的log,直接匹配到X(log和index都匹配),那么就把s3的X后面的log全部给s1(覆盖掉不匹配的log)。

9.raft日志复制

image.png S1S2S3三个节点,每个log是有序的一号二号三号,内容是XTQ,s2是现在的leader,然后index(小箭头)就代表已经commit log,我们看一下一个正常工作流程是如何进行的。
首先,他收到一个leader收到一个新的log K。然后呢,他会把K放到自己的log上,然后呢,他会把K以及当前自己的commit index,同时发送给其他follower,其他的follower都会发现得到一个新的K,然后呢,我的commit index也往前进了一步,这个index之前leader确定新的commit index就会同步给follower。 follower而发现自己commit index往前推进了,就会发现已经有新的值达成共识了,下面的状态机就会把commit index之前那些没读的值写入。然后呢,当此外呢,当follower拿到新的log之后,他会告诉leader,我已经拿到新的值了,这个时候leader发现这个K已经在多数派的这个磁盘上确定了,那leader就认为这个K永远不会丢,就把这个K就认为他已经commit了,然后把commit index往后推。

10.raft从节点失效

image.png s3失效时,S1S2跟原来一样,会把K复制从leader复制给follower,把commit index复制过去,然后再下一步这个K 就commit了,因为三个点两个多数派就可以正常走,说明系统容错,S3即使一直挂掉,整个raft可以继续往前commit log,那对于S3来讲,他log已经和leader不一致了,我怎么把它补全呢?其实是有一个迭代的过程。就是首先S2会像S3发送一个K,说K前面的index是3,我要给你发送3后面的log K,但是s3没有3,就往前推,发2后面的T,但也没有3,就再往前,1号后面的,s3有1,且1是x,就补全s3。

四.回到KV

1.利用Raft,重新打造KV

image.png

问题:

  • Raft不保证一直有一个Leader
  • 只保证一个term至多一个leader
  • 可能存在多个term的leader

2.一致性读写的定义

方案一:

  • 写log被commit了,返回客户端成功
  • 读操作也写入一条log,状态机apply时返回client
  • 增加Log量
    方案二:
  • 写log被commit了,返回客户端成功
  • 读操作先等待所有commited log apply,再读取状态机
  • 优化写时延
    方案三:
  • 写log被状态机apply,返回给client
  • 读操作直接读状态机
  • 优化读时延

3.确定合法的Leadership

  • 方案一:通过一轮Heartbeat确认Leadership(获取多数派的响应)
  • 方案二:通过上一次Heartbeat时间来保证接下来的有段时间内follower不会timeout,同时follower在这段时间内不进行投票,如果多数follower满足条件,那么在这段时间内则保证不会有新的leader产生 结合实际情况旋转方案二/三

image.png