分布式理论 | 青训营笔记

81 阅读16分钟

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

一、本堂课重点内容

  • 分布式概述
  • 系统模型
  • 理论基础
  • 分布式事务
  • 共识协议
  • 分布式实践

二、详细知识点介绍

分布式概述

什么是分布式

分布式系统是计算机程序的集合,这些程序利用跨多个独立计算节点的计算资源来实现共同的目标。

分类

  • 分布式计算
  • 分布式存储
  • 分布式数据库
  • ......

优势

  • 去中心化
  • 低成本
  • 弹性
  • 资源共享
  • 可靠性高

挑战

  • 普遍的节点故障
  • 不可靠的网络
  • 异构的机器与硬件环境
  • 安全

Why-How-What

即为什么要用分布式系统,怎么使用,用来干什么。

从使用者视角

Why
  • 数据爆炸,对存储和计算有大规模运用的诉求
  • 成本低,构建在廉价服务器之上
How
  • 分布式框架
  • 成熟的分布式系统
What
  • 理清规模,负载,一致性要求等
  • 明确稳定性要求,指定技术方案

从学习者视角

Why
  • 后端开发必备技能
  • 帮助理解后台服务器之间协作的机理
How
  • 掌握分布式理论
  • 了解一致性协议
What
  • 把要点深入展开,针对难点搜索互联网资料学习
  • 将所学知识运用于实践

常见的分布式系统

分布式存储

  • Google File System(GFS):Google分布式文件系统
  • Ceph:统一的分布式存储系统
  • Hadoop HDFS:基于GFS架构的开源分布式文件系统
  • Zookeeper:高可用的分布式数据管理与系统协调框架

分布式数据库

  • Google Spanner:Google可扩展的、全球分布式的数据库
  • TiDB:开源分布式关系型数据库
  • HBase:开源NoSQL数据库
  • MongoDB:文档数据库

分布式计算

  • Hadoop:基于MapReduce分布式计算框架
  • Spark:在Hadoop基础之上,使用内存来存储数据
  • YARN:分布式资源调度

系统模型

故障模型

概述

  • Byzantine failure:节点可以任意篡改发送给其他节点的数据
  • Authentication detectable byzantine failure(ADB):Byzantine failure的特例;节点可以篡改数据,但是不能伪造其他节点的数据
  • Performance failure:节点未在特定时间段内收到数据,即时间太早或太晚
  • Omission failure:节点收到数据的时间无限晚,即收不到数据
  • Crash failure:在Omission failure的基础上,增加了节点停止响应的假设,即持续地Omission failure
  • Fail-stop failure:在Crash failure的基础上增加了错误可检测的假设

以下为故障模型的洋葱模型

image.png

故障与故障模型

故障描述可能的类型
磁盘故障如:磁头不寻道、盘片不转、磁介质损伤等。年发生率1%2%1\%-2\%Fail-stop
磁盘坏道、坏块磁头划伤引起坏道,或受宇宙射线影响晶体管产生位反转Fail-stop,ADB
服务器主板、板卡故障可能是风扇故障,或灰尘引起的短路,或SCSI/RAID卡造成的死机Crash
网络故障电源故障、背板故障等,网卡位反转、网络流量大造成大量丢包等Byzantine,Omission
网络分区网络引起节点形成不同的子集,子集中网络相通,子集间网络不通Performance
内存故障内存出错造成的数据被篡改,分为UE、CE两种ADB
线缆故障服务器光模块频繁up或downPerformance,Omission
内核崩溃内核内部的致命错误,产生的kernel panicCrash
CPU故障年故障率接近1%1\%Omission,Crash
电源故障服务器失去电力支撑Omission
软件故障如:进程Crash、内存踩坏、状态不一致、配置错误、软件bug等Byzantine,Crash等

拜占庭将军问题

具体问题可通过互联网查询。

得到的结论是:当有 3m+13*m+1 个将军,其中 mm 个是叛徒时,可以增加 mm 轮协商,最终可达成一致。

共识和一致性

最终一致性

客户端A读到x=0时,当客户端C正在写入时,客户端A和B可能读到0或者1。但是当C写入完成后,A和B最终能读到一致的数据。称这样的一致性成为Eventually consistent(最终一致性)

image.png

线性一致性

当客户端A读到更新的版本x=1后,及时将消息同步给其他客户端,这样其他客户端立即能够获取到x=1。称这样的一致性为Linearizability(线性一致性)

image.png

如果要保证“线性”一致性,多个节点之间势必要进行协商,以寻求一致。这样就会增加延迟,系统的可用性便会受损。

时间和事件顺序

happened before

定义 happened beforehappened \space before 关系,记为 \rightarrow ,其满足如下三个条件:

  • 如果事件 aabb 是在相同节点上的两个事件,aabb 之前发生,则有 aba \rightarrow b
  • 如果事件 aa 表示某个节点发送某条消息,bb 是另一个节点接收这条消息,则有 aba \rightarrow b
  • 如果有 aba \rightarrow bbcb \rightarrow c,则有 aca \rightarrow c

当且仅当 aba \nrightarrow bbab \nrightarrow a 时,称两个事件为并发的(Concurrent)。

如下图所示,可以推导出 p1r4p_1 \rightarrow r_4,具体路径为 p1q2q4r3r4p_1 \rightarrow q_2 \rightarrow q_4 \rightarrow r_3 \rightarrow r_4

image.png

Lamport逻辑时钟

对于每一个节点 PiP_i ,定义时钟 CiC_i 为一个函数,它为任意的事件 aa 赋值编号为 Ci(a)C_i(a)

  • 如果 aabb 是在相同节点 PiP_i 上的两个事件, aabb 之前发生,则有 Ci(a)<Ci(b)C_i(a) \lt C_i(b)
  • 如果事件 aa 表示节点 PiP_i 发送某条消息,bb 表示节点 PjP_j 接受这条消息,则有 Ci(a)<Ci(b)C_i(a) \lt C_i(b)

于是,在之前的时空图中可以加入一些虚线,表示 tick linetick \space line,如下图所示

image.png

其中,在同一节点内的连续两个事件之阿金,至少要有一条 tick linetick \space line

利用逻辑时钟,就可以对整个系统中的事件进行全序排序

理论基础

CAP理论

选项描述
C(Consistency)一致性,指数据在多个副本之间能够保持一致的特性(严格的一致性)。
A(Availability)可用性,指系统提供的服务必须一直处于可用的状态,每次请求都能获取到非错的响应,但是不保证获取的数据为最新数据。
P(Partition Tolerance)分区容错性,分布式系统在遇到任何网络分区故障时,仍然能够对外提供满足一致性和可用性的服务,除非整个网络环境都发生了故障。

CAP理论往往运用于数据库领域,同样可以适用于分布式存储方向。

其中CAP三者可两两组合得到如下三种特征:

  • CA 放弃分区容错性,加强一致性和可用性,其实就是传统的单机数据库的选择
  • AP 放弃一致性(强一致性),追求分区容错性和可用性,例如一些注重用户体验的系统。
  • CP 放弃可用性,追求一致性和分区容错性,例如与钱财安全相关的系统

image.png

那CAP三者可不可以共存?

答案是不行,因为P,分区容错性只能和C和A两者之一结合。试想这样一种场景:服务器A和服务器B,刚开始访问A、B服务器都是可用的,并且数据也是一致的,在满足分区容错性时,要求A、B服务器之间任意一方出现故障时,另一方仍然能够提供服务。此时假设A、B服务器之间的通信网络断开,然后用户向B发送请求,更新了数据,而由于A、B服务器之间的通信网络断开,A服务器就不能获得最新的数据,因此若向A服务器请求数据,此时就有两种选择:

  • 牺牲一致性,保证可用性,将旧数据返回
  • 牺牲可用性,保证一致性,阻塞等待,直到恢复

image.png

其中,保证可用性的近似解决方法是:把故障节点的负载转移给备用节点负责。如下图所示:

image.png

因此,三者之间是不可共存的。

ACID理论

事务是数据库系统中非常重要的概念,它是数据库管理系统执行过程中的一个逻辑单元,它能够保证一个事务中的所有操作要么全部执行,要么全都不执行。

数据库事务拥有四个特性ACID,即分别是

  • 原子性Atomic)

    指的是事务包含的所有操作要么全部成功,要么全部失败回滚,强调操作

  • 一致性Consistency)

    指的是事务必须使数据库从一个一致性状态变换到另一个一致性状态,也就是一个事务执行之前和执行之后都必须处于一致性状态,强调状态。 其与CAP理论中的一致性的区别是:

    • ACID中的一致性就类似于每次转账操作之后,账户总和金额是不变的
    • CAP中的一致性指的是各节点之间的数据是是一致的
  • 隔离性Isolation)

    隔离性是当多个用户并发访问数据库时,数据库为每一个用户开启的事务,不能被其他事务的操作所干扰,多个并发事务之间要相互隔离。

  • 持久性Durability)

    指的是一个事务一旦被提交了,那么对数据库中的数据的改变就是永久的,即便是在数据库系统遇到故障的情况下也不会丢失提交事务的操作。

BASE理论

Base理论是对CAP理论中一致性和可用性权衡的结果,其来源于对大型互联网分布式实践的总结,是基于CAP理论逐步演化而来的。其核心思想是

  • Basically Available(基本可用)

    假设系统出现了不可预知的故障,但是还能用,相比较正常的系统而言,会在响应时间或功能上有所损失

  • Soft state(软状态)

    允许系统中的数据存在中间状态,并认为该状态不影响系统的整体可用性,即允许系统在多个不同节点的数据副本存在数据延时。

  • Eventually consistent(最终一致性)

    系统能够保证在没有其他新的更新操作的情况下,数据最终一旦能够达到一致的状态,因此所有客户端对系统的数据访问最终都能够获取到最新的值。

image.png

分布式事务

顾名思义,在分布式系统中实现事务

二阶段提交

二阶段提交(Two-phase Commit):为了使基于分布式系统架构下的所有节点在进行事务提交时保持一致性而设计的一种演算法。

三个阶段:

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

Coordinator2(Coordinator)--Commit-->Participant-A2(Participant-A)
Coordinator2--Commit-->Participant-B2(Participant-B)
Participant-A2--Ack-->Coordinator2
Participant-B2--Ack-->Coordinator2

如上图所示,左边为准备(Prepare)阶段,右边为提交(Commit)阶段

异常情况

  • Coordinator不宕机,Participant宕机。如下图所示需要回滚操作(左为Prepare阶段失败,右为回滚操作),在Prepare阶段,如果某个事务参与者反馈消息失败,说明该节点的本地事务执行不成功,必须回滚
graph TD
Coordinator1(Coordinator)--Prepare-->Participant-A1(Participant-A)
Coordinator1--Prepare-->Participant-B1(Participant-B)
Participant-A1--Done-->Coordinator1
Participant-B1--Fail-->Coordinator1

Coordinator2(Coordinator)--Rollback-->Participant-A2(Participant-A)
Coordinator2--Rollback-->Participant-B2(Participant-B)
Participant-A2--Ack-->Coordinator2
Participant-B2--Ack-->Coordinator2
  • Coordinator宕机,Participant不宕机。此时需要选取新的协调者,待查询状态后,重复二阶段提交
  • Coordinator宕机,Participant宕机。此时无法确认状态,需要数据库管理员介入,防止数据库进入一个不一致的状态

注意问题

  • 性能问题

    两阶段提交需要多次节点间的网络通信,耗时过大,资源需要进行锁定,徒增资源等待时间。

  • 协调者单点故障问题

    如果事务协调者节点宕机,需要另外选举一个新的协调者,否则参与者处于中间状态无法完成事务。

  • 网络分区带来的数据不一致

    一部分参与者收到了Commit消息,另一部分没收到Commit消息,会导致节点间数据不一致。

思考题

  1. 日志被保存在可靠的存储设备上,如何保证这一点?

    答:单机情况下,EMC的存储可以做到。分布式情况下,建立一层分布式文件系统或者分布式块存储或分布式KV系统。

  2. 参与者Commit了,但是Ack消息协调者每收到,怎么办?

    答:回滚。

三阶段提交

三阶段提交 vs 两阶段提交

将两阶段提交中的Prepare阶段,拆成两部分:

  • CanCommit

    先查询是否能提交

  • PreCommit

    可提交就进入准备阶段

时序图如下所示

sequenceDiagram
Coordinator->>Participants:CanCommit(询问是否可以执行)
Participants->>Coordinator:可以则进入PreCommit阶段;失败或超时则退出
Coordinator->>Participants:PreCommit(询问是否可以执行)
Participants->>Coordinator:可以则进入DoCommit阶段;失败或超时则Rollback
Coordinator->>Participants:DoCommit(向所有人提交事务请求)
Participants->>Coordinator:Ack(反馈提交结果)

解决了两个问题:

  • 单点故障问题

    一个节点发生故障,在较前阶段就可以检测出来

  • 阻塞问题

此外,三阶段提交还引入了超时机制,在等待超时后,可以自动提交事务。

MVCC

在MVCC之前,通常使用的方式来保证数据的并发读写,而锁从大类上又可以分为以下两种:

  • 悲观锁

    操作数据时,直接把数据锁住,直到操作完成后才会释放锁,在上锁的期间其他人不能修改数据

  • 乐观锁

    不会上锁,只是在执行更新时判断别人是否修改数据,只有冲突时才放弃操作。

由以上可知,锁虽然能够实现并发控制,但是其效率比较低,因此MVCC也就被提出来了。

MVCC是一种并发控制的方法,维持一个数据的多个版本使读写操作没有冲突。所以既不会阻塞写,也不会阻塞读。MVCC为每个修改保存一个版本,和事务的时间戳相关联。可以提高并发性能,解决脏读问题。

共识协议

Quorum NWR模型

Quorum NWR三要素

  • N

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

  • W

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

  • R

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

为了保证强一致性,需要保证W+R>N,即保证读能读到最新数据。(必要不充分)

graph TD
副本1-->Read
副本2-->Read
副本3-.->Read(Read)
Write(Write)-.->副本1
Write-->副本2
Write-->副本3

如上图所示,N=3,W=2,R=2N=3,W=2,R=2,写入副本2和副本3,从副本1和副本2里读,总能读取到最新值(副本2)。

不过只适用于Append Only的存储,若修改原来的数据则可能导致拿不到最新的数据,如下表所示。

副本1副本2副本3说明
初始值v1=1v1=1v1=1v1=1v1=1v1=1初始全为1
第一次写v2=2v2=2v2=2v2=2v1=1v1=1修改副本1和副本2
第二次写v3=3v3=3v2=2v2=2v2=2v2=2修改副本1和副本3

RAFT协议

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

相关概念

  • Leader - 领导者

    通常一个系统是一主(Leader)多从(Followers)。Leader负责处理所有的客户端请求,并向Follower同步请求日志,当日志同步到大多数节点上后,通知Follower提交日志。

  • Follower - 跟随者

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

  • Candidate - 备选者

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

image.png

  • Log(日志)

    节点之间同步的信息,以只追加写的方式进行同步,解决了数据被覆盖的问题

  • Term(任期号)

    单调递增,每个Term内最多只有一个Leader

  • Committed

    日志被复制到多数派节点,即可认为已经被提交

  • Applied

    日志被应用到本地状态机,也就是执行了log中的命令,修改了内存状态

image.png

Leader选举过程

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

两个规则

  • 在一个任期内每个参与者最多投一票
  • 要成为Leader,必须拿到多数投票

Log Replication过程

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

  1. Leader收到写请求w
  2. 将w写入本地log
  3. 向其他Follower发起AppendEntries RPC
  4. 等待多数派回复
    • 更新本地状态机,返回给客户端
    • 下一个心跳通知Follower上一个Log已经被Committed了
    • Follower也根据命令应用本地状态机
  5. Follower有问题,Leader一直retry

切主

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

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

Stale读

发送Leader切换时,old leader收到了读请求。如果直接响应,可能会有Stale Read,该如何解决?

解决方案:保证读的强一致性

读操作在lease timeout内,默认自己是leader;不是则发起一次heartbeat。等待Commit Index应用到状态机。

Election timeout > lease timeout:新leader上任,自从上次心跳之后一定超过了 Election timeout,旧leader大概率能够发现自己的lease过期

三、实践练习例子

本次的课程给出了两个实践的例子

  • 一个是MapReduce来对数据进行统计,其中Mapper将输入分解为多个Job来并行处理。彼此之间几乎没有依赖关系。Shuffle将mapper结果打乱,防止数据倾斜。Reducer对map阶段的结果进行全局汇总
  • 另一个是分布式KV,即根将海量结构化数据根据Key分成不同的Region,每个Region构建一个单机KV数据库,Region之间形成Raft Group,做到强一致。

四、课后个人总结

本次的课程收获很大,学习了很多分布式相关的理论和系统模型,并了解了分布式事务的处理方法,还学习了一些共识协议,最后通过两个小例子将本课程连接了起来。

五、引用参考