• 2020-05-01

    一种区分读写操作的对工程实践友好的分布式一致性协议

    Views: 13947 | No Comments

    一种区分读写操作的对工程实践友好的分布式一致性协议

    摘要

    流行的共识算法如 Paxos[1] 和 Raft[2] 被广泛地应用于分布式系统中, 用于实现一致性. 但是, 这些算法本质上并不区别读和写操作, 因而给工程实践带来了极大的困扰. 这种困扰导致在具体实现时, 开发者往往基于性能优化方面的考虑, 对这些算法做没有经过理论验证的更改.

    我想在区分读写操作的前提下, 设计一种对工程实践友好的分布式一致性协议, 能适应读多写少或者读少写多等不同场景的需求. 这个分布式协议基于 Quorum 机制, 采用 2PC 策略和复制状态机模型, 适用于有强一致性要求的分布式数据库系统和其它分布式系统.

    Continue reading »

    Posted by ideawu at 2020-05-01 13:12:06 Tags: ,
  • 2020-04-28

    分布式一致性算法的全部内涵

    Views: 11904 | 1 Comment

    第一步, 查询集群的状态;

    第二步, 根据状态判断如果没有不一致就返回, 有的话要么主动修复, 要么等待别人修复;

    这是一致性的基础, 也是一致性的全部. 注意, 某些类型的不一致可以不修复. 怎么写? 当然是尝试全写, 过半数正面确认就"成功"返回给客户端, 过半数在"准备阶段"拒绝就是"失败", 否则就是"未知".

    Paxos 是怎么做的? 甭管有没有不一致, 先修复再说.

    [完]

    Posted by ideawu at 2020-04-28 11:32:31
  • 2020-04-28

    Paxos什么都包含, 也什么都不是

    Views: 12608 | No Comments

    初学 Paxos, 你会觉得 Paxos 非常高深隐晦, 接着会觉得没什么难的, 最后估计会觉得 Paxos 什么都不是, 就跟空气一样既是基础, 又不算什么.

    你说写多数派或者全写, 这是谁都知道的道理, 不是 Paxos 独创.

    你说读多数派, 显然这是错误的, 必须全读. 读多数派得出的结论, 和读全部节点得出的结论, 很可能是不同的, 甚至两次读不同的多数派也可能得出不同的结论, 所以不是一致性的.

    所以, 要么全读, 要么"读修复 read-repair", 也就是在读的时候修复数据, 这就是 Paxos 所谓的 phase 2 - 产生新的共识. 读修复就是 Paxos 的基础.

    这些读和写策略, 都不是 Paxos 独创, 可以算 Paxos 头上, 也可以不算. 你可以说我在用 Paxos, 也可以说我用的不是 Paxos. Paxos 非常强大, 又什么都不是.

    你优化来优化去, 读的时候不修复(非一致性 quorum read), 或者先等一等别人修复(read index), 都丢失了 Paxos 的基础, 那还算 Paxos 吗?

    Posted by ideawu at 11:07:22 Tags:
  • 2020-04-27

    Raft Read Index 的实现

    Views: 12987 | No Comments

    首先, 获取"集群"的 ReadIndex. 注意, 不是某个节点上面的某个状态, 而集群的多个节点共同确定的一个变量. 这个变量如何获取, 下面说明.

    func GetClusterReadIndex(){
        foreach(peers as peer){
            i = peer.rpc_GetLastIndex();
            ret = max(ret, i);
            if(recv_majority){
                break;
            }
        }
        return ret;
    }
    

    获取至少半数以上成员的 LastIndex, 也就是每个节点持久化的最新一条日志的 index. 一条日志只要在一个节点上被持久化, 那么这条日志要么被集群 commit, 要么被覆盖之后再 commit, 没有其它的选项.

    由此可见, ReadIndex 不一定已经被 commit, 但一定会被 commit, 最终也将会 apply. 所以, 拿到集群的 ReadIndex 之后, 我们只需要等, 等它被任意一个节点 apply 即可.

    ReadIndex = cluster.GetReadIndex();
    while(1){
        foreach(peers as peer){
            if(ReadIndex <= peer.LastApplied()){
                return peer.GetValue();
            }
        }
    }
    

    不断地重复轮询全部节点, 如果谁 apply 了, 就以谁的值为准. 还有一种方案, 那就是等本地的状态机 apply, 这样避免网络传输.

    ReadIndex = cluster.GetReadIndex();
    while(1){
        if(ReadIndex <= self.LastApplied()){
            return self.GetValue();
        }
    }
    

    还有一种优化方案, 是客户端判断. 客户端请求多数节点, 获取 max pending 和 max committed, 如果 committed 大于等于 pending, 那么取对应的 value. 如果 pending 大于 committed, 说明集群中有"脏写"(未决的数据), 客户端要做的就是等待. 如果不等待, 那就做 Paxos phase 2 所讲的"读修复".

    没什么玄幻的, 道理非常简单: 第一步, 查询集群的状态; 第二步, 根据状态判断没有脏数据就返回, 有的话要么主动修复, 要么等待; 这是一致性的基础, 也是一致性的全部.

    Posted by ideawu at 2020-04-27 19:22:40 Tags:
  • 2020-04-27

    Paxos和Raft读优化 – Quorum Read 和 Read Index

    Views: 13236 | No Comments

    在优化之前, 我们先分析原始的能保证正确性的做法是什么, 然后分析问题在哪, 最后再讨论怎么优化.

    * Paxos 读流程: 针对要读取的对象, 在其操作序列中追加一条 read 共识.
    * Raft 读流程: 在 Raft 日志序列中写一条 read 日志, 等 commit.

    两者其实是一样的, 都是复制状态机模型, 都要复制一条 read 日志, 然后 apply 之后才能返回结果. 问题在哪呢? 很显然, 大部分系统是读多写少, 每一次读都需要达成共识, 而达成共识的过程无论是 Paxos 还是 Raft, 成本都太高了 - 网络写 IO, 磁盘写 IO. 既然是读操作, 能否只用磁盘读 IO 就行了呢? 这个想法就是优化的方向.

    一致性算法的基础是, 无论客户端是什么操作, 集群都要在全部或者部分成员参与的情况下, 达成新的共识. 优化的思路是在某些情况下, 不用每一次操作都达成新共识. 怎么做到? 1, 如果全部成员都是一致的, 客户端读操作就没必要生成新的共识. 2, 读操作不主动生成新的共识, 而是等待其它客户端写操作生成新的共识.

    Linearizable Paxos Quorum Read

    流程分为: Quorum-read phase, Rinse phase.

    1. 读取半数以上节点的 accepted_index, 记录其中 max_accepted_index
    2. 等待, 直到任意一个节点 max_accepted_index 被 apply/execute, 以那个节点上的数据返回给客户端

    重要的地方就是"等待", 如果没有等待, 就会变成简单的 Quorum Read, 是错误的. 这个读优化算法的正确名字应该是: 带有停止等待的多数派读. 区别于传统意义的"读多数派中的最新者". 其实叫"Read Index"更准确.

    这里面其实有个问题, 因为 Paxos 是消极的, 如果没有外界触发, 这个等待可能永远也不会返回.

    Raft Read Index

    Leader 的数据就是可以返回的, 唯一的问题是, 某个节点可以错误地自认为是 leader, 所以要先确认自己是不是 leader.

    1. 自认是 leader 的节点询问半数以上节点自己是不是 leader
    2. 如果确认自己是 leader, 等状态机 apply 到大于等于 commitIndex(这个时刻就是 readIndex) 后, 读取状态机的结果返回
    3. 如果发现自己不是 leader 怎么办? 找到正确的 leader, 获取 readIndex, 然后等待自己的状态机 apply 到至少 readIndex.

    因为 Raft 是积极的, 所以AppliedIndex 一定会最终等于 commitIndex, 即使没有外界触发也不会永远等待.

    无论是原始在往日志序列中追加日志, 或是 Linearizable Paxos Quorum Read 或 Raft Read Index, 本质就是插入一个同步点(memory barrier), 确保这个同步点和之前的日志已经 apply.

    Posted by ideawu at 18:11:50 Tags: ,
  • 2020-04-26

    Paxos vs Raft 的争论

    Views: 13445 | No Comments

    业界对用 Paxos 还是用 Raft 争论(讨论)非常多. 我认为有争论是有好处的, 但要弄清楚为什么争论, 以及争论的点在哪. 否则, 上升到偏见或者信仰, 就没有意义了. 例如, 隔几天来一个"我为什么抛弃XX选择YY?", 或者借助"洋大人"之口(其实就是一个普通的外国程序员)说出什么"Why we choose XX over YY?", 那就非常可笑了.

    我认为, 对 Paxos 和 Raft 的争论, 大多是基于软件工程上模块职责划分的见解不同, 也就是一个系统中, 共识模块(Paxos 和 Raft 都是共识算法)应该做什么功能, 加哪些限制条件.

    Paxos, 我们首先要限制必须是 Basic Paxos, 否则没有争论的意义. Basic Paxos 本身是赤裸裸的, 限制少, 灵活, 因为它是基础中的基础. 也正因为太基础了, 所以脱离群众, 离真正实用太远. 这也是为什么这么多年, 业界没有一个真正意义上的开源的 Paxos 编程语言库.

    Raft 是这么多年, 对 Paxos 工程实践的总结和提炼, 以学术研究(论文)的方式加以证明, 并提供了工程指导. 所以, 这才是为什么有那么多的 Raft 开源库, 而大家的代码结构又大同小异的原因. 因为, 幸福的家庭都是相似的, 不幸的家庭各有各的不同.

    我总结一下, Paxos 和 Raft 的争议点在有哪些, 这些争议点是职责划分的问题, 你很快就会发现.

    1. 单主还是多主

    "单主"是很多人不选择 Raft 的原因(没什么所谓选择不选择 Paxos, Paxos 就是基础). 一是多写入点, 客户端可以随机选取任何一台服务器来接收请求, 所以, 客户端的代码非常简单, 配置服务器的 ip:port 列表, 用随机算法或者 round robin 算法选一台创建 socket 连接即可. 二是故障恢复时间, Paxos 把故障恢复隐含到了每一次请求当中, 不像 Raft 那样明确的划分职责, 独立出一个选主过程. 独立的选主过程占用独立的时间片, 阻塞正常请求, 所以理论上要增加故障时间.

    但是, Raft 当然可以优化成在每一次请求都选主, 工程实践上没问题, 但是, 这不就成了 Basic Paxos 了吗? 所以, 没人这么做. 大多数情况下就是这样的, Paxos 加了限制就成了 Raft, 而 Raft 做了优化就变成了 Paxos. 向谁靠拢的选择而已.

    2. 顺序提交还是乱序提交

    这是争论最多的地方. 事实上, 一个系统必然有乱序(并发)的地方, 同时也会存在顺序(串行)的地方, 没有任何一个大型的系统只包含并发或者只包含串行, 不可能, 我在工程上没遇见过这样的系统. 问题就在于, 你想把并发(岔路口)开在哪?

    举一个例子, 网络编程中, 你可以在 accept 之后就启动线程, 每个线程处理一个 socket, 也就是你把并发的岔路口开在了这里. 你当然也可以用 IO 多路复用(如 epoll), 在一个线程中顺序地(但不阻塞)地读取 socket, 然后在读完请求之后, 启动线程处理请求, 也就是, 你把并发的岔路开在了那里.

    Paxos vs Raft 就是这样的例子, Raft 认为把串行的部分交给我, 然后你(状态机)再并发. 但是用 Paxos 的人认为, 关于是串行还并行, 应该由我(状态机)来决定, 共识算法没必要加这个限制. 孰优孰劣? 任何一个理性和聪明的人都能得出答案.

    用 Paxos 的人, 希望自己把控更多的东西, 所以 Paxos 非常薄, 薄得几乎不存在, 也就没有所谓的 Paxos 库了, 因为它的职责太少, 以致于根本不值得独立成一个库. 用 Raft 的人相反, 把更多的职责加给 Raft, 不重新发明轮子.

    所以, 通过一两个反例来说明 Paxos 好还是 Raft 好, 在技术上非常幼稚. 正确的说法是: 某个职责不应该交给这个模块, 因为某个职责交给另一个模块可以做一些优化. PS: 工程上优化并不是唯一的考量因素.

    Posted by ideawu at 2020-04-26 12:54:33 Tags: ,
|<<<123456789>>>| 7/9 Pages, 52 Results.