分布式理论(二)|青训营笔记

68 阅读6分钟

这是我参与「第五届青训营」伴学笔记创作活动的第 10 天

1. 概述

我将主要介绍如下知识点:

  1. 分布式事务
  2. 共识协议

2. 分布式事务

在微服务架构中,分布式事务一般的解决办法就是 两阶段提交 或者 三阶段提交,不管使用哪种办法都存在事务失败,导致数据不一致的情况,关键时刻还得人工去恢复数据

如果分布式事务涉及的节点很多,某一个节点的网络出现异常会导致整个事务处于阻塞状态,大大降低数据库的性能

所以一般情况下,尽量少用分布式事务

2.1 两阶段提交(Two-phase Commit)

定义

  • 为了使基于分布式系统架构下的所有节点在进行事务提交时保持一致性而设计的一种演算法

基于三个假设

  • 引入协调者(Coordinator)和参与者(Participants),互相进行网络通信
  • 所有节点都采用预写式日志,且日志被写入后即被保持在可靠的存储设备上
  • 所有节点不会永久性损坏,即使损坏后仍然可以恢复

正常流程

  • 第一阶段:Prepare 阶段
  • 第二阶段:Commit 阶段

可能出现的情况

  • 情况1:Coordinator 不宕机,Participant 宕机

    • 即 Prepare 阶段失败 -> 需要进行回滚 操作

    回滚:在 Prepare 阶段,如果某个事务参与者反馈失败消息,说明该节点的本地事务执行不成功,必须回滚

  • 情况2:Coordinator 宕机,Participant 不宕机

    • 可以重新启用新的协调者,待查询状态后,重复二阶段提交
  • 情况3:Coordinator 宕机,Participant 宕机

    • 无法确认状态,需要数据库管理员介入,防止数据库进入一个不一致的状态

两阶段提交需解决的问题

  • 性能问题

    • 需要多次网络通信,资源需要等待并锁定
  • 协调者单点故障问题

    • 如果事务协调者宕机,需要另起新的协调者,否则参与者处于中间状态无法完成事务
  • Commit 阶段网络分区带来的数据不一致

    • 非所有节点都收到 Commit 请求,会导致节点之间的数据不一致

2.2 三阶段提交

三阶段提交 vs 两阶段提交

  • 将两阶段提交中的 Prepare 阶段,拆成两部分:CanCommit 和 PreCommit 机制

    • CanCommit阶段:询问是否可以执行

    • PreCommit阶段:重新确认是否可以执行

    解决了两个问题:单点故障问题和阻塞问题

  • 另外引入了超时机制,在等待超时之后,会继续进行事务的提交

  • DoCommit 阶段:向所有人提交事务

2.3 MVCC

悲观锁和乐观锁

  • 悲观锁:操作数据时直接把数据锁住,直到操作完成后才会释放锁;上锁期间其他人不能修改数据
  • 乐观锁:不会上锁,只是在执行更新时判断别人是否修改数据,只有冲突时才放弃操作

乐观锁适合在读多写少的场景中使用,而悲观锁适合在读少写多的场景中使用

MVCC

  • 是一种多版本并发控制的方法,维持一个数据的多个版本使读写操作没有冲突。所以既不会阻塞写,也不阻塞读,提高并发性能的同时也解决了脏读的问题

版本的选取:使用物理时钟或逻辑时钟

  • 物理时钟:提供TrueTime API,有Master节点维持一个绝对时间,保证各个服务器之间时钟误差控制在ϵ内,通常ϵ<7ms。
  • 逻辑时钟:中心化授时的方式--时间戳预言机(TSO),好处是无需硬件的支持

3. 共识协议

3.1 Quorum NWR 模型

Quorum NWR模型将CAP的选择交给用户,是一种简化版的一致性模型

三要素

  • N:在分布式存储系统中,有多少份备份数据

  • W:代表一次成功的更新操作要求至少有w份数据写入成功

  • R: 代表一次成功的读数据操作要求至少有R份数据成功读取

    为了保证强一致性,需要保证 W+R>N

    可以用反证法证明:假如 W + R <= N,即 R <= N - W,由于 W 代表新写入的数据的份数,故 (N - W) 代表老数据的份数,所有 R <= N - W 就意味着读取的数据有可能是老数据,就不满足强一致性,所以假设不成立,故需 W+R>N

引起的并发更新问题

  • 如果允许数据被覆盖,则并发更新容易引起一致性问题

3.2 RAFT 协议

概述

  • Raft协议是一种分布式一致性算法(共识算法),即使出现部分节点故障,网络延时等情况,也不影响各节点,进而提高系统的整体可用性。Raft 是使用较为广泛的分布式协议

三种角色

  • Leader - 领导者

    • Leader 负责处理所有的客户端请求,并向 Follower 同步请求日志,当日志同步到大多数节点上后,通知 Follower 提交日志
  • Follower - 跟随者

    • 不会发送任何请求,接受并持久化 Leader 同步的日志,在 Leader 告知日志可以提交后,提交日志
    • 当 Leader 出现故障时,主动推荐自己为 Candidate
  • Candidate - 备选者

    • Leader 选举过程中的临时角色。向其他节点发送请求投票信息。如果获得大多数选票,则晋升为 Leader

四种定义

  • Log(日志):节点之间同步的信息,以只追加写的方式进行同步,解决了数据被覆盖的问题
  • Term(任期号):单调递增,每个Term内最多只有一个Leader
  • Committed:日志被复制到多数派节点,即可认为已经被提交
  • Applied:日志被应用到本地状态机:执行了log中命令,修改了内存状态

Leader 选举过程

  • 初始全部为 Follower
  • Current Term + 1
  • 选举自己
  • 向其它参与者发起 RequestVote 请求,retry 直到
    • 收到多数派请求,成为 Leader,并发送心跳
    • 收到其它 Leader 的请求,转为 Follower,更新自己的 Term
    • 收到部分,但未达到多数派,选举超时,随机 timeout 开始下一轮

两个规则

a> 在一个任期内每个参与者最多投一票(持久化)

b> 要成为 Leader,必须拿到多数投票

Log Replication 过程

新 Leader 产生,Leader 和 Follower 不同步,Leader 强制覆盖Followers 的不同步的日志

切主

当 Leader 出现问题时,就需要进行重新选举

  • Leader 发现失去 Follower 的响应,失去 Leader 身份
  • 两个 Follower 之间一段时间未收到心跳,重新进行选举,选出新的 Leader,此时发生了切主
  • Leader 自杀重启,以 Follower 的身份加入进来

Stale 读

  • 发生 Leader 切换,old leader 收到了读请求。如果直接响应,可能会有 Stale Read

3.3 Paxos 协议

Paxos 算法与 RAFT 算法区别

  • Multi-Paxos 可以并发修改日志,而 Raft 写日志操作必须是连续的
  • Multi-Paxos 可以随机选主,不必最新最全的节点当选Leader

优劣势

  • 优势:写入并发性能高,所有节点都能写
  • 劣势:没有一个节点有完整的最新的数据,恢复流程复杂,需要同步历史记录

4. 参考

  • 字节内部课:分布式理论 - 现代架构基石