这是我参与「第五届青训营 」伴学笔记创作活动的第 5 天
分布式概述
什么是分布式
分布式系统是计算机程序的集合,这些程序利用跨多个独立计算节点的计算资源来实现共同的目标。
- 分布式计算
- 分布式存储
- 分布式数据库
- 等等
优势:
- 去中心化
- 低成本(一台强劲服务器不如卖多台服务器便宜)
- 弹性(服务于用户,用户的资源使用存在波峰波谷)
- 资源共享
- 可靠性高
逐层递进,越往后越难实现
挑战:
- 普遍的节点故障
- 不可靠的网络(导致了不确定性)
- 异构的机器与硬件环境
- 安全
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):节点可以篡改数据,但不能伪造其他节点的数据(特定情况下的拜占庭)
- Performance failure:节点未在特定时间段内收到数据,即时间太早或太晚(如果数据太早太晚,还不如寻找下一可用节点,因为你不清楚该节点是否可用以及恢复时间有多长)
- Omission failure:节点收到数据的时间无限晚,即收不到数据
- Crash failure:节点停止响应,持续性的故障(只知道状态不知道原因)
- Fail-stop failure:错误可检测,是最容易处理的故障(与 Crash failure 不同,既知道状态也知道原因)
拜占庭将军问题
-
两将军问题
-
定义:
- 两支军队的将军只能派信使穿越敌方领土互相通信,以此约定进攻时间。该问题希望求解如何在两名将军派出的任何信使都可能被俘虏的情况下,就进攻时间达成共识
-
结论:
- 两将军问题是被证实无解的电脑通信问题,两支军队理论上永远无法达成共识
-
方案:
- 同时发送 N 个信使,任何一个到达对方军队,都算成功
- 设置超时时间,发送后未在一定时间返回,则加派信使
-
TCP是两将军问题的一个工程解(在两个方向确认包的序列号,增加超时重试)
-
-
三将军问题:
- 两个“忠将”A和B,一个“叛徒”C,互相传递消息,消息可能丢失,也可能被篡改,当有一个将军是“叛徒”(即出现拜占庭故障)时,整个系统无法达成一致。
- 由于“叛徒”C的存在,将军A和将军B获得不同的信息。这样将军A获得2票进攻1票撤退的信息,将军B获得1票进攻2票撤退的信息,产生了不一致
-
四将军问题:
-
将军D作为消息分发中枢,约定如果没收到消息则执行撤退
-
步骤:
- 如果D为“叛徒”,ABC无论收到任何消息,总能达成一致
- D为“忠将”,ABC有2人将D的消息进行正确的传递,同样能保证最终决策符合大多数。
-
进而能够证明,当有3m+1个将军,m个“叛徒”时,可以进行m轮协商,最终达成一致
-
共识和一致性
假设存在 A、B、C 三个节点
数据原本值为 0
在 C 写入 1 时,由于读取时机不一样,写前读到仍旧是 0,而写后读取到为 1
不过,只要 A、B 两节点保持持续读取,最终仍然会得到一致的结果,故称其为 最终一致性
而在另一种假设中,我们 A 或是 B 在读取到更新后的版本后,及时将该值共享至其它节点,这样达成的一致性称为线性一致性
然而,维持线性一致性存在代价,多节点势必进行协商以保证数据一致,这会增加额外开销增大延迟,系统可用性受损了
时间和事件顺序
1978年Leslie Lamport发表《Time, Clocks, and the Ordering of Events in a Distributed System》
happened before 和并发
该文章定义了计算机系统中的时间和事件顺序,引入happened before和并发的定义,可以以此对分布式系统中的事件进行推导
定义 happened before 关系并记为 → ,其满足三个条件:
- 如果a和b是在相同节点上的两个事件,a在b之前发生,则定义:a→b
- 如果事件a表示某个节点发送某条消息,b 是另一个节点接受这条消息,则有a→b
- 如果有a→b且b→c ,则有a→c
当且仅当a、b相互之间都没有 happened before 关系时,我们称两个事件为并发(concurrent)
Lamport逻辑时钟
这个概念在分布式理论中具有革命性的意义,帮助我们在一系列分布式事件当中梳理出逻辑的先后关系。利用逻辑时钟,我们可以对整个系统中的事件进行全序排序
对于每一个节点Pi我们定义时钟Ci为一个函数,它为任意的事件a赋值编号为Ci(a)
- 如果a和 b是在相同节点Pi上的两个事件,a在b之前发生,则有Ci(a)<Ci(b)
- 如果事件a表示节点Pi发送某条消息,b表示节点Рj接受这条消息,则有Ci(a)<Cj(b)
于是我们可以在时空图中加入类似右图虚线所示的“tick line"
在同一节点内的连续两个事件之间,至少要有一条tick line
利用逻辑时钟,我们可以对整个系统中的事件进行全序排序
理论基础
CAP理论
CAP理论往往运用于数据库领域,同样可以适用于分布式存储方向
- CA:放弃分区容错性,加强一致性和可用性,其实就是传统的单机数据库的选择
- AP:放弃一致性(这里说的一致性是强一致性),追求分区容错性和可用性,例如一些注重用户体验的系统
- CP∶放弃可用性,追求一致性和分区容错性,例如与钱财安全相关的系统
完全的 CAP 是不可能做到的,但是可以尽力逼近或做一些取舍
在保证 P 的情况下(分区容错),C 和 A 的取舍都会导致一定的问题:
AP系统
故障发生时,为了保证可用性,允许不同进程读到不同的数据
CP系统
故障发生时,为了避免读到不一致的数据,可能拒绝访问
在网络发生分区的情况下,我们必须在可用性和一致性之间做出选择。近似解决办法︰把故障节点的负载转移给备用节点负责。允许一个进程作为Master,其他进程作为Backup,当故障时将请求转移给Backup进行处理
就算假如出现了同步和 master 节点同时出现问题的情况,也只会损失未同步这一段时间里的数据,且 master 节点恢复后数据可以被找回
ACID理论
ACID:原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)和持久性(Durability)
BASE理论
Base理论是对CAP中一致性和可用性权衡的结果,其来源于对大型互联网分布式实践的总结,是基于CAP定理逐步演化而来的。其核心思想是∶
- Basically Available(基本可用):假设系统,出现了不可预知的故障,但还是能用,相比较正常的系统而言︰响应时间上的损失,或功能上的损失
- Soft state(软状态)∶允许系统中的数据存在中间状态,并认为该状态不影响系统的整体可用性,即允许系统在多个不同节点的数据副本存在数据延时。
- Eventually consistent(最终一致性)∶系统能够保证在没有其他新的更新操作的情况下,数据最终一定能够达到一致的状态,因此所有客户端对系统的数据访问最终都能够获取到最新的值。
大多数情况下,系统对于 C 的要求并不是很高,所以可以尽量多向 AP 系统靠,BASE 理论也是偏向于 AP 系统的
分布式事务
两阶段提交
两阶段提交(Two-phase Commit ) :为了使基于分布式系统架构下的所有节点在进行事务提交时保持一致性而设计的—种演算法。
三个假设
- 引入协调者( Coordinator)和参与者( Participants ),互相进行网络通信
- 所有节点都采用预写式日志,且日志被写入后即被保持在可靠的存储设备上
- 所有节点不会永久性损坏,即使损坏后仍然可以恢复
正常流程分为 prepare 和 commit 两个阶段
可能出现的异常情况
在提交前任意 Participant 本地事务出错就回滚
- Coordinator不宕机,Participant宕机。如下图所示,需要进行回滚操作
- Coordinator宕机,Participant不宕机。可以起新的协调者,待查询状态后,重复二阶段提交(原本 Coordinator 中保存有状态信息,但是宕机了,所以只能新起一个查询各协调者判断状态)
- Coordinator宕机,Participant宕机。此时只能让 DBA 介入,防止数据库进入不一致的状态
两阶段提交需注意的问题
- 性能问题 两阶段提交需要多次节点间的网络通信,耗时过大,资源需要进行锁定,徒增资源等待时间。
- 协调者单点故障问题 如果事务协调者节点宕机,需要另起新的协调者,否则参与者处于中间状态无法完成事务。那么该如何确定状态并选出新协调者
- 网络分区带来的数据不一致 一部分参与者收到了Commit消息,另一部分参与者没收到Commit消息,会导致了节点之间数据不一致。
假如参与者Commit了,但Ack信息协调者没收到,仍旧会选择进行回滚,会造成资源的浪费
三阶段提交
实际上就是将两阶段中的 Prepare 阶段拆分为 CanCommit 和 PreCommit 阶段:
- CanCommit阶段:询问是否可以执行
- PreCommit阶段:重新确认是否可以执行
- DoCommit阶段:向所有人提交事务
在两阶段中,如果出现无法提交的错误操作会额外导致一次回滚,而预先询问的三阶段提交则可以避免问题
另外,三阶段提交还引入了超时机制,在等待超时之后会继续事务的提交(一定程度上缓解 ack 未收到导致的额外回滚问题)
解决问题
- 单点故障问题
- 阻塞问题
三阶段缓和了两阶段面临的问题,但依然没有解决:
- 性能问题
- 网络分区场景带来的数据—致性问题
MVCC
MVCC是一种并发控制的方法,维持一个数据的多个版本使读写操作没有冲突。所以既不会阻塞写,也不阻塞读。MVCC为每个修改保存一个版本,和事务的时间戳相关联。可以提高并发性能,解决脏读的问题。
悲观锁和乐观锁
- 悲观锁:操作数据时直接把数据锁住,直到操作完成后才会释放锁;上锁期间其他人不能修改数据
- 乐观锁:不会上锁,只是在执行更新时判断别人是否修改数据,只有冲突时才放弃操作(CAS 就是典型的利用了乐观锁)
版本的选取
- 物理时钟:提供TrueTime API,有Master节点维持一个绝对时间,保证各个服务器之间时钟误差控制在ϵ内,通常ϵ<7ms。
- 逻辑时钟:中心化授时的方式--时间戳预言机(TSO),好处是无需硬件的支持