这是我参与「第五届青训营 」伴学笔记创作活动的第 7 天
分布式概述
什么是分布式
Why? How? What
常见的分布式系统
系统模型
故障模型
按照故障处理难度进行划分。
- Byzantine failure:节点可以任意篡改发送给其他节点的数据,是最难处理的故障
- Authentication detectable byzantine failure (ADB):节点可以篡改数据,但不能伪造其他节点的数据
- Performance failure:节点未在特定时间段内收到数据,即时间太早或太晚
- Omission failure:节点收到数据的时间无限晚,即收不到数据
- Crash failure:节点停止响应,持续性的故障
- Fail-stop failure:错误可检测,是最容易处理的故障
总结 故障处理难度从低到高
其中 Fail-stop 是出错并且知道原因、Crash是出错但是不知道原因。
Omission和Performance都是未决状态,但是Omission明确是个故障,而Perforamance是故障和非故障的叠加态,真正的未决。
而对于最后两个拜占庭问题,都是正确性方面的故障,不知道数据是真的还是假的,解决起来难度太大,一般的分布式系统不予以解决,而是通过加密等方式来保证信息不会被篡改。
常见故障类型划分
拜占庭将军问题
拜占庭两将军问题
简述
拜占庭帝国的两个将军率领军队与敌军作战,两只部队中间夹着敌军,且单独一只军队进攻对军一定会失败,需要两军互相派出通信兵通信,协商统一进攻时间。但是通信兵穿越敌军的过程中可能会被俘虏,导致通信过程受阻。问,需要派出多少通信兵,才能保证两者协商一致。
解答
无论派出多少通信兵,两者都不能够协商一致。
因为,假如A派出通信兵与B协商,B收到后派出通信兵告诉A说同意了,但是通信兵被截获了,B出兵了,A却没有,没有达成共识。而A如果再加一次通信(三次握手),但是A此时出兵了,如果A派出的通信兵迟迟没有到,B仍然不会出兵。达不成共识。
工程解
两个解决方案(将出错可能降到最小)
- 一次派出N个通信兵,总有一个能够到
- 设置超时重传,一段时间没收到回复,就重发。
比如 TCP 就是通过 三次握手 + 重传机制,给出了拜占庭问题的工程解。
拜占庭将军问题(三人及以上)
拜占庭帝国有多个将军,每个将军独立的观察敌军,并给了投降 / 进攻的投票,最后汇总结果,根据占比半数以上的方案实施。
但是这些将军中间有叛徒,叛徒会篡改发给其他将军的消息。导致投票结果出错。
解答
对于 3 m + 1 个将军,其中有m个叛徒,可以通过 m 次协商可以达到共识。
比如4个将军A、B、C、D,其中有一个叛徒,而D将军只负责统计票数,没有投票权。
第一轮协商时,A、B、C将自己的选择交由D将军,由D将军统计结果,并将结果发还给A、B、C。
之后,A、B、C进行第二轮投票。
这样操作之后,无论D是不是叛徒,最后都一定可以达成共识。
因为
假如 D 是叛徒,那么D发给A、B、C的消息可能是错的,因为消息可能会被篡改,又因为只有一个叛徒,所以,A、B、C都是忠臣,那么第二轮协商时,没有叛徒了,一定可以得到正确的结果。
假如D 是忠臣,并且只有一个叛徒,那么D发给A、B、C的消息,起码有2个是正确的,再第二轮协商时,投票环节,两个正确的票大于叛徒手里那一张票,所以也可以得到正确的结果。
大部分的分布式系统,都没有解决拜占庭问题(区块链解决了)。
共识和一致性
共识
多个服务器间通过协商,达成共同约定,就叫做共识。
最终一致性
所有的数据最终读到的都是一致的。也就是发生修改后,一段时间内,其他节点读到的数据可能是旧的,也可能是新的,但是最终都会读到最新的数据。
线性一致性
只要有一个服务器读到最新的数据,就会立马同步给其他所有客户端,不会有数据摇摆这种情况的发生。此外,线性的意思是,读到的数据是连续的,不中断,不回退的。不会有客户端读到了新数据,之后还有客户端读到老数据这种数据回退情况发生。
时间和事件顺序
Happen Before
- 在同一节点,如果该节点上的A事件发生在B事件之前,那么就是说 A -> B
- 在不同节点,如果A事件发送一个消息,B事件接受一个消息,那么也可以说 A -> B
- 对于 A -> B -> C,有A -> C。当且仅当,A 不happenbeforeB,并且B 不happenbofore A的时候,才说A、B是并发的。
可以通过这条原则,推导不同节点上的事件的 happenbefore 关系。
lamport逻辑时钟
对于每个节点Pi,我们都定义一个(事件的函数)时钟函数Ci,它为任意的事件a赋值为Ci(a),也就是x的值是事件编号,y是lamport时间戳。
并规定
- a -> b,那么一定有Ci(a) < Ci(b)
- b -> a,那么一定有Ci(a) > Ci(b)
如图,可以在图中加入虚线(tick line)
要求
- tick line之间的时间间隔一致
- 一个节点的 tick line 中只能有一个事件。
理论基础
CAP理论
简介
- C 一致性
- A 可用性
- P 分区容错性 分区中没有办法同时具有百分百具备CAP,三个特性。
划分
CA
放弃分区容错性,比如单机数据库。
CP
放弃绝对的可用性,保证一致性。常用于金融、钱财相关的应用。
AP
放弃绝对的一致性(弱一致性),保证可用性。常用于注重用户体验的产品。
应用
常用在数据库领域,以及分布式存储领域。
工程解
提供服务的多个节点,其中一个节点为 Master 节点,负责处理所有请求,其余节点为 backup 节点,只负责备份 Master 节点同步过来的数据。Master 节点会在检查点时,将自身数据同步给 backup 节点。
当 Master 节点出现故障时,可以直接切换 backup 节点为新的 Master 节点,代替 Master 节点处理请求。中间切换需要时间,但可以保证一定的可用性。
而如果Master出现故障时,检查点之前的数据已经同步给backup服务器,数据自然不会丢失,但检查点之后写入的数据会丢失。也就保证了一定的一致性。
ACID理论 CA系统
略
BASE理论 AP系统
简介
BASE理论是大型互联网公司分布式实践的总结,是通过CAP理论逐渐演变过来的。BASE理论适用于AP系统,也就是注重可用性,因为大部分不与钱财相关的公司,都用不到那么高的可用性。
内容
- Basically Available 基本可用
- 假设系统发生了不可预知的错误,但是还是能用,只是可能服务降级。
- Soft State 软状态
- 允许系统中出现中间状态,也就是允许不同的客户端访问到不同的数据(新、旧数据)
- Eventually Consistent 最终一致性
- 最终所有客户端都能访问到新的数据。
分布式事务 CP系统
二阶段提交
简介
二阶段提交是分布式事务的一种实现方式。
前置条件
- 节点有两个角色,Coordinator(协调者) 和 Participant(参与者)
- 所有节点都采用预写式日志,且日志写入完成后会被记录在可靠介质上
- 所有节点都不会永久损坏,也就是损坏后都能修复
过程
提交分两个阶段:
- Prepare阶段
- 协调者会向参与者发送Prepare请求。
- 参与者收到Prepare请求,并进行处理。
- 如果处理成功,会发回给协调者 Done 响应。
- 如果处理失败,会发回 Fail 响应。
- Commit阶段
- 协调者根据 Prepare 阶段返回的请求,选择回退还是提交。
- 如果第一阶段所有参与者返回的都是Done,选择提交。
- 如果第一阶段任一参与者响应Fail,选择回退。
- 选择回退,会向参与者发送 Rallback 请求。
- 选择提交,会向参与者发送 Commit 请求。
- 参与者执行完后会向协调者发送 ACK 响应。
- 协调者根据 Prepare 阶段返回的请求,选择回退还是提交。
可能遇到的问题
- 参与者宕机、协调者未宕机。
- 二阶段提交的第二阶段,协调者会发送 Roolback 请求进行回滚
- 参与者未宕机,协调者宕机。
- 会重新选择新的协调者
- 参与者和协调者都宕机。
- 需要数据库管理员手动管理。
缺点
- 性能开销大
- 两阶段提交需要访问多个节点,带来额外开销
- 协调者单点故障
- 协调者一旦宕机,需要另起一个协调者,中间会造成服务停摆。
- 网络分区问题
- 一旦部分参与者收到了Commit请求,一部分参与者没收到?这时候只能选择回滚,会造成数据不一致,只能回滚。(一般事务提交过程全程加锁,拒绝读操作的,所以这部分中间数据不会被读到,但是会造成阻塞问题)
思考
- 如何满足第二个前置要求,将预写日志保存到可靠介质上
- 因为分布式可能使用廉价服务器,所以不从硬件方面解决,大多数解决方案都是使用分布式文件存储系统,KV存储系统。
- 如果有的ACK收到了,有的没收到,怎么办
- 回滚。
三阶段提交
简介
二阶段提交的优化版,不过只是缓解了二阶段提交遇到的问题
阶段
- CanCommit
- 询问是否可执行CanCommit,是,继续,不是(节点发生故障),结束/推出。
- PreCommit
- 询问是否可执行PreCommit,是,继续,不是,RollBack。
- DoCommit
- 执行,向所有节点提交事务,并接收ACK
改进
因为提交事务前,会先询问事务是否可以被执行,如果是,才执行,这就避免了很多,事务加锁并执行到最后阶段,才发现问题,需要重做事务,要节省非常多的内存。
缺点
- 性能
- 网络分区问题