最近一年左右在研究系统架构和分布式系统. 今天跟大家一起分享讨论. 群里有技术和其他领域背景的, 所以不会讲的很深入和靠近细节. 尽量让大部分人理解.
今天给大家简单分享一下几种分布式系统的一致性算法, 常见的有 Paxos, Raft, ZAB.
一, 分布式系统
首先简单说一下分布式系统. 这是一个比较宽泛的概念. 他的产生和出现是为了应对和解决以往系统的问题.
我的简单理解是一套互相连接共同向外提供服务的系统, 系统内部节点间通过某种方式进行通信. 常见的大的如电商网站, 小的如一个数据库集群, 缓存集群等.
在分布式系统出现之前使用的单机系统, 一旦服务器的操作系统, 硬盘, 网络等出现问题, 就可能会出现服务中断, 数据丢失等问题. 另外, 单机的性能再高, 存储能力再扩展, 也是有限的, 而分布式系统可以包含大量的服务器用来分散带宽, 存储和计算压力. 第三, 由于跨较远区域甚至跨国的网络延迟有时还是客观的, 所以服务器集中在一个地方很难向较大范围提供较好的服务和体验.
百度上对分布式系统的解释:
分布式系统 (distributed system) 是建立在网络之上的软件系统. 正是因为软件的特性, 所以分布式系统具有高度的内聚性和透明性.
个人对这里的内聚性和透明性的理解是:
内聚性: 系统内的节点, 服务目的, 分工, 规则明确.
透明性: 分布式系统对外提供服务, 调用该系统的外部用户不用考虑塔式怎么工作的, 只要根据规则进行使用就成, 也不需要知道里面包含多少台计算机, 都在什么地方.
同时由于不完美的运行环境, 他本身需要解决一些问题.
如: 系统中使用的每个节点都可能出现单点故障突然不工作了, 节点间会有或大或小, 忽大忽小的网络延迟, 节点间的网络也有可能出问题. 从技术角度看, 可能发生的问题基本上认为早晚会发生. 说到底很多问题就是概率和成本收益的平衡. 因此, 一个分布式系统要正常提供对外的服务, 它的设计本身就要考虑到这些问题的解决. 在算法层面就是由分布式系统的一致性算法来辅助解决和应对这些问题, 确保系统尽可能的对外正常的提供服务.
这里说尽可能的意思是分布式系统不能确保永远完全没有问题, 尤其是一些极端情况. 比如一个 3 个节点的数据库集群, 单节点挂掉的概率较小, 3 个节点同时挂掉的概率极小极小, 不过也存在这种可能, 不过概率很小, 一般不考虑. 或者机房停电, 光缆被挖段, 如果几个节点都在一个机房, 那就不能工作了. 今天讲的一致性算法主要解决的是从系统的角度分析一个分布式系统内部节点本身和节点间出现问题的解决. 跟这些外部问题关联较小, 那些场景要同时借助其他手段来缓解和解决.
二, 一致性问题
比较容易理解的常见的实际问题: 一个系统包含 5 个节点, 由于网络原因其中 3 个节点和另 2 个节点失去了联系, 这时候系统应该怎么处理, 是否还可以继续提供服务.
如果继续提供服务, 他们之间的数据就是不一致的, 后面节点间的网络恢复后, 怎么解决数据不一致的问题.
节点间的网络延迟, 磁盘读写, CPU 处理能力不同, 保存到这个系统中的数据在哪些情况下可以认为保存成功了, 还要以一种效率较高的方式来实现.
三, ZAB 协议
3.1 ZAB 的基本特点
把节点分两种, Leader(主)和 Follower(从). 有一个主节点, 所有写操作全部通过节点进行处理, 如果一个从节点收到了一个写操作请求, 就会转给主节点处理. 其他节点都是从节点, 可以通过从节点进行读操作. 主节点通过选举得出, 主节点失踪后, 其他从节点自动开始选举新的主节点.
注意, 这里写操作可以认为是保存或修改现有数据. 读操作可以认为是读一个现存的数据出来. 读操作相对简单一些, 这里主要讲写操作, 其流程为:
客户向主节点请求写数据 X.
主节点为该数据生成一个唯一的递增 Id, 叫 ZXIDX(Id).
主节点把 X(Id)发给所有的从节点, 跟他们确认能不能正常把数据记录下来, 这个操作叫 Propose 提议;
超过一半的节点向主节点回复没问题, 这个回复操作叫 ACK 应答
节点收到一半以上从节点的肯定答复后, 给所有的从节点发送确认提交请求, 表示你们可以把这个数据保存下来了, 同时自己也正式保存这个数据, 这个过程叫 Commit 提交.
现在问题来了, 如果没有超过一半的从节点给主节点回复 ACK. 那么主节点就不能确认该消息可以成功记录下来. 为啥一定要收到超过一半的从节点的答复? 答案是为了 ++ 确保数据的一致性 ++. 一般分布式一致性算法可以确保在部分节点挂掉的情况下保持数据的一致性, 在大部分节点都同时挂掉的情况不能确保.++ 比如: 一共 5 个节点, 2 个挂掉没问题, 其中可以包括主节点, 如果 3 个挂掉了, 就不能确保了 ++. 一般出现这种情况时, 比如 5 个节点只剩了 2 个, 这两个节点就不再提供服务了.
3.3 工作流程
比如一个银行的存款系统: 3 个节点在上海, 2 个节点在广州. 一个账号 Adun, 有 1K 的钱在里面. 5 个节点都包含这个数据. 这时上海广州之间的网络挂了. 我连到上海的节点把钱转给一个人, 然后连到广州的节点把里面的 1K 钱转给另一个人. 他们之间是断开的, 如果同时提供转账服务, 就出现了数据一致性的问题. 而如果只有 3 个节点的上海提供服务就没有这个问题了. 另一个问题是, 他们之间的网络恢复后, 以谁的数据为准呢? 所以大部分的分布式一致性算法都是采用大多数认同的方式. 当然, 在写数据的时候如果强制确认所有节点都写入了新数据会更安全和一致, 但是系统的可用性和性能就大大降低了, 只要有一个节点挂了, 系统就不工作了.
我们分析下子节点挂掉的情况. 先看节点挂掉数占比情况分析: 少于一半的子节点挂掉没有任何影响. 达到一半的子节点挂掉系统不能提供服务, 一般这种情况概率很小.
一个子节点挂掉一段时间后又恢复了之后, 先通过主节点同步新的数据, 因为自己挂了一段时间, 很可能没有最新的数据. 数据同步之后正式成为工作的子节点开始工作. 添加一个新的子节点到一个现有的集群, 也是这个过程.
如果是主节点挂掉, 情况会复杂一点. 主节点挂掉后, 集群暂时不提供写服务, 开始新的主节点选举. 选举规则如下:
发消息到每一个自己还能连上的节点: 包含自己的节点编号叫 myid 和 自己保存的数据的最大的 ZXID.
谁的 ZXID 最大, 谁就是新的主节点. 为什么呐? 因为这表示他的数据最新. 尽量确保数据一致性.
ZXID 相同的时候, 谁的 myid 最大谁是新的主节点;
收到其他节点的数据后, 跟自己的判断, 如果对方比自己的大, 就认同对方为主节点.
得到一半以上(注意这里又是一半以上) , 节点认同的候选的节点成为新的主节点.
新的主节点选举出来之后, 进入集群的数据同步节点, 先检查集群内部哪些节点的数据比自己旧, 把数据同步过去. 然后开始向外部提供服务. 在新的主节点选出来之前, 集群不能提供写服务. 这里的步骤有好几步, 在正常的服务器环境下, 这个过程是很快的, 几十到几百毫秒.
回到刚才的上海和广州的场景. 广州的发现上海的节点失踪了, 这时候开始选举新的主节点, 但是它们只有 2 个节点, 选不出来主节点, 也不提供服务. 上海的继续服务. 网络恢复后, 把数据同步到广州节点然后继续工作.
这是 ZAB 协议的主要信息. 还有很多细节, 感兴趣的话可以去看看. 不过一般分布式系统的一致性算法都有点复杂. ZAB 是为了支撑 ZooKeeper 服务设计出来的, ZooKeeper 是给其他服务提供集群管理服务的, 所以他本身要足够健壮. ZAB 协议的全称就是 Zookeeper Atomic Broadcast, 即 ZooKeeper 原子性广播协议.
四, Raft 算法
下面再简单介绍几句 Raft 算法. Raft 算法很多地方跟 ZAB 很像, 也分主节点和从节点, 主节点选举的地方有一点差别.
发现主节点失踪一段时间后, 所有从节点向其他从节点发消息, 让他们选自己为新的主节点;
还没参加选举的节点如果收到其他节点的选举请求, 就选举自己收到的第一个节点, 后面谁再请求自己支持选举, 就告诉他们我已经支持另一个节点了
如果一个节点发现另一个节点得到的支持比自己多, 也就开始无条件支持那个节点选举, 同时让支持自己的节点也去支持它
如果一轮没选出来得到大多数节点支持的主节点, 就开始下一轮选举, 直到一个节点得到了大部分节点支持, 成为新的主节点;
五, Paxos 算法
还有一个叫 Paxos 的一致性算法, 更复杂, 惭愧我目前还不敢确保完全理解, Paxos 算法还衍生出来好几种变体, 经典的 Paxos 算法不分主节点和从节点.
其实现在大部分的一致性算法都是起源于或参考了 Paxos 算法, 有人说只有 Paxos 才是真正的一致性算法.
来源: http://www.tuicool.com/articles/UnENFnv