在我看来包含 log, state machine, consensus algorithm 这3个部分, 并且是有 electing, normal case, recovery 这3个阶段都可以称为paxos 协议一族.

    为什么说raft 也是3个阶段, 因为其实raft 在重新选举成leader 以后, 也是需要一段recovery 的时间, 如果超过半数的follower 没有跟上leader 的日志, 其实这个时候raft 写入都是超时的, 只不过raft 把这个recovery 基本和normal case 合并在一起了, zab 不用说有一个synchronization 阶段, multi-paxos 因为选举的是任意一个节点作为leader, 那么需要有一个对日志重确认的阶段

    1. 是否支持乱序提交日志和乱序apply 状态机

      其实这两个乱系提交是两个完全不一样的概念.

      是否支持乱序提交日志是区分是raft 协议和不是raft 协议很重要的一个区别

      在我看来multi-paxos 与raft 对比主要是能够乱序提交日志, 但是不能够乱序apply 状态机. 当然也有paxos 实现允许乱序apply 状态机, 这个我们接下来说, 但是乱序提交日志带来的只是写入性能的提升, 是无法带来读性能的提升的, 因为只要提交的日志还没有apply, 那么接下来的读取是需要等待前面的写入apply 到状态机才行. 并且由于允许乱序提交日志, 带来的问题是选举leader 的时候, 无法选举出拥有最多日志的leader, 并且也无法确认当前这个term/epoch/ballot 的操作都提交了, 所以就需要做重确认的问题. 因此raft/zab/vsp 都是要求日志连续, 因为新版本的zab 的FLE 算法也是默认选举的是拥有最长日志的节点作为leader

      支持乱序apply 日志是能够带来读取性能的提升, 这个也是Generalized Paxos 和 Egalitarian Paxos 所提出的做法. 但是这个就需要在应用层上层去做这个保证, 应用层需要确定两次操作A, B 完全没有重叠. 如果大量的操作都互相依赖的话, 这个优化也很难执行下来. 换个角度来考虑的话, 其实支持乱序 apply 日志, 其实是和multi group 类似的一个做法, 都是在应用层已经确定某些操作是互相不影响的. 比如PhxPaxos 团队就是用的是multi group 的做法, 所以有些宣传raft 不适合在数据库领域使用, 其实我觉得有点扯, 乱序提交日志带来的收益其实不高, 想要乱序apply 状态机的话, multi group 基本是一样的

    2. 是否支持primary order

      具体讨论:

      所以我认为 tcp + 顺序apply 状态机都能够做到单个tcp连接级别的FIFO order, 但是如果需要支持 client 级别的FIFO order, 那么就需要在client 上记录一些东西.

    3. 在选举leader 的时候, 是否支持 designated 大多数. 选举leader 的时候如何选举出leader

      designated 有什么用呢? 比如在 VSR 的场景里面, 我们可以指定某几个节点必须apply 成功才可以返回. 现实中的场景比如3个城市5个机房, 那么我们可以配置每个城市至少有一个机房在这个designated 里面, 那么就不会出现有一个城市完全没有拷贝到数据的情况.

      如何选举出leader 这个跟是否支持乱序提交日志有关, 像raft/zab/vsr 这样的场景里面, 只能让拥有最长日志的节点作为leader, 而paxos 可以在这里增加一些策略, 比如让成为leader 的节点有优先级顺序等等

    4. 读取的时候的策略, 包括lease 读取或者在任意一个replica 读取, 那么可能读到旧数据. 然后通过版本号进行判断是否读取到的是最新数据.

      lease 做法其实和协议无关, 任意一种的paxos 都可以通过lease 的优化读取的性能, 当然lease 做法其实是违背分布式系统的基础理论, 就是分布式系统是处于一个asynchronization network 里面, 无法保证某一条消息是到达还是在网络中延迟

      目前在raft phd thesis 里面读取的优化就包含了 lease 读取和只需要通过Heartbeat 确认leader 信息进行读取

      如何尽可能减少leader 的压力, 是一致性协议都在做的一个事情, 想zookeeper 这种通过上层应用去保证, 允许读取旧数据也是一个方向, 当然还有的比如像Rotating leader, Fast Paxos 给client 发送当前的proposal number 的做法.

    5. 在检测leader 是否存活的时候是单向检测还是双向检测

      比如在raft 里面的心跳只有leader 向follower 发送心跳, 存在这样的场景. 如果有5个节点, 只有leader 节点被割裂了, 其实4个节点网络正常, 新的这4个节点选举出一个leader, 旧的leader 由于完全与其他节点割裂, 所有的AppendEntry 都是失败的, 不会收到一个新的Term号, 因此一直保持着自己是leader 的状态. 那么这样系统就会同时存在两个leader, 但是这个不影响协议正确性, 因为旧的leader 是无法写入成功的.

      在zab 里面心跳是双向的, 也就是说leader 向follower 发送心跳, 如果超过半数的follower 没有应答, 那么leader 会进入到electing 状态. 同时follower 也会向leader 发送心跳, 如果leader 没有回应, 那么这个follower 节点同样会进入到electing 状态. 那么对应于上述的场景, zab 就不会出现像raft 一样, 长期同时存在两个leader 的情况.

      通过上述对比, 我还是觉得raft 实现更简洁, 而双向心跳检测这种做法增加了大量的复杂度

    最后的结论是这样

    对于计算密集型任务, 也就是写入量比较大的场景, 建议选择passive replication strategy, 那么也就是选择VSR 或者 ZAB 这样.其实主要就是考虑到passive replication 只需要更新的是state 的修改, 而不是用于操作, 那么就可以少做很多操作.

    对于对性能比较敏感的场景, 那么应该选择active replication stategy, 那么也就是选择mulit-paxos, raft 这样, 并且不需要designated majorities. 因为passive replication strategy 在rocovery 需要更多的时间, 因为client 的操作是需要写入到状态机, 如果这个client 的操作最后没有被提交, 因为log 可以提供一个回滚操作, 而状态机很少能够提供这种回滚操作, 因此就需要将这个节点的状态机的内容重写, 所以会导致recovery 需要较长的时间.

    Reference:

    1. http://www.tcs.hut.fi/Studies/T-79.5001/reports/2012-deSouzaMedeiros.pdf
    2. http://www.read.seas.harvard.edu/~kohler/class/08w-dsi/chandra07paxos.pdf
    3. http://baotiao.github.io/2017/11/08/state-machine-vs-primary-backup/