PacificA 一致性协议解读
公司数据库的一致性协议用的是 PacificA,相信更多人对 raft 比较了解(毕竟是许多大学钦定的教学算法,不曾了解过的可以看这里 快速理解一致性协议 raft),这篇文章写给懂 raft 的读者。
PacificA 的 paper 在 08 年左右发出来的,比 Raft 早了 6,7 年。
在 PacificA 论文中,他们强调该算法使用范围是 LAN (Local Area Network),讲白了就是对跨机房不友好。
不管是 ZAB,Raft,还是 PacificA,都是基于实用主义,把 log replication 和 leader election 分开,不像 paxos,把两个问题合在一起想。
log replication
日志复制流程与 raft 相似:每条日志具有一个序列号(serial number),primary 复制日志给 secondaries,secondary 接收到后返回 ack。
唯一不同的是,PacificA 要求收到 所有节点的 ack 后,该请求才算提交,而在 raft 中,只需过半节点(quorum)即可提交。这也是该算法不利跨机房的原因,但这种限制很大程度上能够简化协议。
leader election
一般高可用系统会用 zk/etcd 选主,而 zk/etcd 作为管理者本身,则需要用复杂的一致性协议来选主。PacificA 作为一个一致性协议另辟蹊径,不守套路,选择直接用 zk/etcd(用论文的说法叫 global configuration manager)来做选主。
一个一致性协议实现选主的方法是使用另一个一致性协议,这算是一个亮点。因为 zk/etcd 更多是针对元数据存储做的系统,需要做普通数据分布式存储的时候,PacificA 就有其意义了。换句话说,PacificA 只是 保障普通数据高可用的,不会抢占其他一致性协议的市场,它们是互补的关系。
PacificA 中 primary 与 secondaries 之间会使用心跳探活(论文里把 心跳 叫 Beacon)。与 Raft 类似的,secondary 探测不到 primary,就会向 configuration manager 申请 换主。
PacificA 的 leader election 以 membership reconfiguration (成员变更)的方式来进行,这点很不一样,因为 reconfiguration 在其他一致性协议里是一个比较难的事情,一般不轻易做。下面细讲。
reconfiguration
由于有 configuration manager 的帮助,reconfiguration 变得比较简单:向 zk 做一次 compare-and-swap 原子配置更新即可。
理论上这种方式可以轻松做到一次性变更多个成员(相比起 raft 要简单许多)。
上面说到,secondary 探测不到 primary,就会向 configuration manager 申请 换主,做法就是要求让 primary 退群;而 primary 探测不到 secondary 时,日志复制无法进行下去,这时 primary 也会申请让 secondary 退群。
这里很显然的一个问题是 secondary 和 primary 互不相通的时候,会互相让对方退群,造成一败俱伤局面。PacificA 在这里做了一个约定: primary 总会先于 secondary 让对方退群。做法也很简单,让 primary timeout < secondary timeout 即可。
alternative
论文里虽然用组内心跳(primary 与 secondaries 之间的 heartbeat)来判活,但我们可以想,这不是必须的:
分布式系统里,每个节点通常会与一个中心资源管理器做心跳,一旦节点挂了,资源管理器就会找一台空闲机器新开节点。
在 PacificA,资源管理器可以作为 configuration manager,同时由于它能够做心跳探活,组内心跳则可以直接省去。换句话说:
资源管理器看到 primary 失活,则进行换主,看到 secondary 失活,则令其退群,primary 和 secondary 之间没有任何心跳。
在节点数较小的场景下,让资源管理器做成员心跳不存在什么压力。
group check
虽然 primary 和 secondaries 之间可以没有组内心跳,但是 绝对不能没有状态同步。
首先 commit point 需要同步。secondary 在收到 commit point 之后,才能够让这部分已提交日志被清理。
其次当前 term / ballot 和当前的 leader / primary 都需要同步。
另外事实上,需要组内同步的可能还包括一些应用相关的状态。在 XiaoMi/pegasus中,这一负责组内同步的 rpc 被称为 group check。我们也用它同步热备份时的备份点。
在 Raft 中,group check 和 heartbeat 的功能被组合在一起,避免长时间无写请求时,节点间状态无法同步。
reconciliation
换主的时候一个问题是新 primary 需要把尚未提交的日志项提交了,算是完成旧 primary 的未竟之事。这个过程叫做 reconciliation。
Implementation
众所周知 Raft 没有讲如何做日志存储的方案,尽管大家看了 logcabin 的 SegmentedLog 以后都知道大概怎么做了。PacificA 在论文里讲了这个问题,因为 PacificA 的 log 也需要考虑 overwriting,log compaction (checkpoint) 。不过一些相关点基本以前在 Raft 里讲过了, neverchanje/consensus-yaraft,这里就不再赘述。