分布式一致性协议杂谈

死线 发布于

早就想写一篇关于分布式一致性协议的文章,奈何自己总是无法「通透」地理解 paxos 这个协议,最近查看了不少资料,总算是对协议本身有了一个更深的认识,写篇文章记录一下。

首先申明一下,本文并不是导读文章,因为关于 paxos 的文章实在太多,根本也不需要普及类的文章,硬要推荐的话,我强烈推荐「微信后台团队」的一系列关于 paxos 的文章,想要找的话请去 github 搜索 phxpaxos,不仅文档丰富,还有应用于工程环境的号称纯 paxos 实现(其实改动还是挺多的),源码的质量还是不错的。由于是初步接触分布式协议,文章的错误纰漏肯定不少,还望批评指正。

paxos究竟解决一个什么样的问题

我觉得很多人和我一样,一开始不太理解 paxos 的出现究竟是为了什么?lamport 的文章举了一个例子,就是「确定一个值」,想一想假设一个有5台机器的集群,如何才能保证一个键值为「key」的数据在所有的5台机器上均拥有值「value」?有人说,那太简单了,依次往5台机器上写入(key, value)不就得了(我暂且叫做它朴素方法)?额,好吧,某种意义上确实是对的,如果你的系统「足够可靠」的话。

两大梦靥

很多简单的课题,一旦在分布式环境下经行部署和实施,就会变得十分的复杂。造成这个问题的原因是分布式系统里有着十分头疼的两个现象,一个是「网络的不可靠」,还有一个是「机器宕机」。所谓的「网络的不可靠」主要是指「网络延时」和「网络丢包」,后者如果用「重发」的方式解决,其实和「网络延时」是等价的。然而这两个现场真的是影响分布式系统数据一致性的罪魁祸首吗?

朴素模型1PC

还是假设一个场景,一个集群里有5台机器 /A/B/C/D/E/,我们需要写入一系列键值为 key(i) 的数据,将其值置为 value(i) ,但是要求最终每台机器上都有相同序列数据集合。在提出复杂的一致性协议之前,我们先构想最简单最暴力的解决方案:

1.引入一个协调节点 coordinator,负责收集所以节点的反馈。

2.coordinator 发送”commit(key, value)”消息到所有的节点告知提交。

3.如果收到所有节点的反馈,准备写入下一个数据,重复1~2。

上述模型显然有问题,但是肯定不是「网络不可靠」带来的,为什么这么说呢,因为即使有延时和丢包,只需要设置一个超时重传即可,只要不考虑效率,等多久都不是问题。最致命的是宕机问题,coordinator和普通节点宕机均会带来问题。

1.coordinator 节点挂了,不言而喻,整个系统就走不了了。

2.普通节点挂了,后来又活了,数据会导致不一致。

很多人把这两种现象当成异常,会导致数据不一致。我却不以为然,因为上述的两个问题并未探寻到问题的核心,比如第一个问题,只要有个勤快的小伙一直盯着机子重新拉起来即可。第二个问题,只需将提交条件放的更加宽松,比如 commit 成功超过一半 coordinator 即可继续下面的动作,宕掉的机器重启后去 coordinator 学习即可。所以我觉得即便是最 naive 的方法也可以保证最终一致性(完全不考虑效率)。

关于2PC和3PC的误会

2PC 和 3PC 是分布式系统里经典的协议,然而它只是针对特定场景使用的,最常见的就是应用在分布式事务上面。在上述的值的确定上,/2PC/3PC/ 和 1PC 并没有什么本质的区别。为什么这么说呢?所谓的 2PC 就是在 commit 之前实现发一个 prepare 消息做「探测」,3PC 也是类似的,只不过是事先做了2次「探测」,任何一次前期探测的失败都会导致数据的写入失败。然而这对于值写入的场景没有任何意义,因为值写入失败的唯一原因就是「宕机」,即使发现了也没有什么挽救的办法。那么这2个协议有什么用呢?那是因为在分布式事务中,事务失败回滚很有可能是因为事务本身不满足数据库事务的某些约束而导致回滚,此时还可以 abort,所以 2PC 和 3PC 是为了保证分布式事务的一致性,和分布式系统里的一致性并没有多大关系。

paxos的出现

有了上述的两个讨论,我自己就下了个结论(很有可能错误,妄指正):

类似 paxos 分布式一致性协议的出现的本质或许并不是为了解决数据的一致性问题,而是为了获得更加/可靠/自动/高效/的服务。

很多人会说,你这不可笑么?分布式一致性协议的出现居然不是为了解决数据一致性?我没有说不是,只是本质上不是这样,因为很多naive的方法也能做到。为了验证我的说法,我们就来谈谈 paxos。

paxos 的出发点之一,就是解决上面说的 coordinator 宕机的问题,为了保证宕机之后不需要人为的拉起,最直观的方法自然是引进复数的机器,最好改个名字叫 proposer,哈哈,是不是越来越眼熟。这一改进说的容易,却带来了一堆问题,即经典的投票问题。

复数的 proposer 你可以想象成你的七大姑八大姨,一堆人在讨论你高考应该填报哪个志愿,当然啦由于没人考虑你自己的意见,恐怕讨论一整天时间都不够。这个就是著名的所谓投票问题:

复数的 proposer 如何获取统一的意见并固化到集群中的每一台机器上。lamport 为了解决这个问题发表了经典的几篇论文,并把协议命名为 paoxs。我这里不细讲这个协议,都被说烂了,只说说几个注意的地方。

1.协议将半数以上定义为大多数并不是随便定的,是为了保证算法的正确性,事实上你可以重新搞个协议,重新定义大多数(比如3/4),更有趣的是这个标准和「集群至少有一半机器存活」条件息息相关。

2.协议本身是二阶段的,为什么需要两阶段本质上是因为拿不到一个全局统一的计数器(这个需要细究才能明白),后面引入 leader 节点之后退化成一阶段协议,然而这个一阶段的过程不是连续的,会被打断;而且,退一万步讲,选主也需要普通 paxos。

3.协议很多约束很宽泛,不是那么严格,所以现在有很多衍生版本,比如/Raft/ZAB/本质是都是 paxos。

4.leader 和 master 并不是一回事,leader 在某些时间点可以有多个,master 严格只有一个。另外,leader 除了是为了解决活锁,还有一个重要的一点,就是提升效率。

5.multi-paxos 其实很多意义上来讲是解决一个效率问题,和正确性有关系,但不全是。

相对于普通的一致性协议,paxos 有什么本质的不同吗?

1.首先要确定本质是什么,其实就最终效果来看,没什么区别。

2.本质上提高了系统的健壮性,机器宕机容忍度明显提升。只要一半意思的系统正常运转,就能运行,再也不需要抓苦力盯着机器是不是宕机了。

3.系统吞吐量上升明显。

总结

本文是我在阅读了几篇论文以及乱七八糟的博文总结的,我知道根本没有多少人能看到最后,即使看也对我乱七八糟的语言组织以及幼稚的结论感到无语,其实我只是至终只想强调一个观点:

paxos 的出现本身或许是为了解决一个工程问题,即在保证系统尽可能多数节点存活的情况下系统仍然能够运转,且满足一致性约束。带着性能的观点去看待 paxos 这个协议,也许能让你更快理解这个协议。

最近在入 phxpaxos 源码坑,不知道能不能跳出来,能的话会写些文字,尽请期待。

估摸着以后看着这篇文章会发笑,哈哈,写着自己看的,大家不要太认真,发现荒唐之处,请不遗余力吐槽~~