• 2020-05-01

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

    Views: 13980 | No Comments

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

    摘要

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

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

    Continue reading »

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

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

    Views: 12618 | 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 2020-04-28 11:07:22 Tags:
  • 2020-04-27

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

    Views: 13269 | 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 2020-04-27 18:11:50 Tags: ,
  • 2020-04-26

    Paxos vs Raft 的争论

    Views: 13468 | 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: ,
  • 2020-04-21

    Paxos学习-instance

    Views: 10450 | No Comments

    在讨论 Paxos 之前, 我想抛出几个包括我在内很多人都会提出的问题:

    * 如何更新一个 key?
    * 如何让两个数据库完全相同(最终一致)?

    先不管最终一致性, 强一致性, 或者什么时间线性一致性顺序一致性. 我的需求是让两台或者更多台机器上的数据库的数据是完全相同的. 经过对隐晦的 Paxos 协议和其相关的资料的来来回回的阅读和理解, 我又一次得出了新的结论...

    Paxos 用来确定一个 instance 的值, 一旦确定, 不可更改!

    什么? 不可更新? 不可修改? 太违反常理了吧? 这样, 有个鸟用? 我今天终于在 stackoverflow 上看到了这个说法, 然后再回头阅读 Paxos made simple, 我不得不得出同样的理解. 所以, 我昨天"关于 Paxos 论文中的迷惑之处"的问题也得到了解答. 如果你把 instance 当作 key, 用 Paxos 来对 key 的值达成共识, 确实只能是一次性的, 确定之后你就不能再更新它的值了.

    如果 Paxos 是这样, 那它几乎没有任何实践意义, 一个不能更新的 key 有什么用? 一个只能写一次的数据库? 问题就在于, 你把 instance 当做什么. 如果要更新 key, 你就不能把 instance 对应成 key, 你应该把 instance 对应成对这个 key 做的一次操作(一条 binlog)... 再看论文最后提到状态机, 这才发觉, 原来, Paxos 天生就是复制状态机模型. 所以, 那些 multi paxos 到底在扯什么鬼?

    只要把 instance 对应成对整个数据库做的一次操作, 然后对多次操作指定顺序, 就是我们常见的 binlog 同步.

    Posted by ideawu at 2020-04-21 22:14:56 Tags:
  • 2020-04-20

    分布式存储名词解析 – 一致性

    Views: 17559 | 5 Comments

    一致性分为访问一致性和数据一致性. 访问一致性, 是指从观察者的角度, 其访问结果是可预期的. 数据一致性是指从上帝视角看, 多个副本的数据是完全相同的.

    Paxos 协议, 这里指的是原始(Basic) Paxos, 其本质是解决访问一致性, 并不解决数据一致性, 它不保证多个副本数据现在是(或者将来是)完全相同的, 它只是在可能不一致的多个副本上提供可预期(一致性)的服务. 这个说法不代表不能使用 Paxos 来保证数据一致性, 而是说, Paxos 的设计目的不在于此.

    Raft 同时解决访问一致性和数据一致性. 使用了 Raft 协议的分布式存储系统, 既能提供可预期的数据访问服务, 也能在时间维度上保证多副本数据一致性. 例如, 在系统隔断外部输入的情况下(不再接受写请求), 经过一定的时间(一般从几毫秒到数秒)之后, 使用了 Raft 协议的集群内的多个副本的数据将是完全相同的.

    Paxos 协议本身并不能保证系统级的数据的最终一致性, 因为它是基于外界触发的消极全量数据拷贝协议. 如果没有外界输入触发, Paxos 就不拷贝数据, 所以它是消极的. 当需要拷贝时, 它拷贝的是全量的数据, 如果你需要保证整个数据库在多台机器上相同, 你应该拷贝整个数据库, 这显然没有实际意义. 另外, 当拷贝结束时, Paxos 并不保证数据被拷贝到所有节点, 这是第二个消极. 所以, 仅仅使用 Paxos 协议的系统, 在隔断外部输入的情况下, 如果数据不一致, 那么过一百年, 多个副本的数据仍然不一致.

    相对比, Raft 的数据拷贝是自动的, 不仅仅依赖外界输入触发, 还依赖时间触发, 所以, Raft 是积极的. 同时, 拷贝结束时, Raft 保证数据被拷贝到了所有节点, 从而实现所有副本是完全相同, 如果达不到这个目的, Raft 就不会停止(不达目的绝不罢休), 这是第二个积极.

    前面说过, 你一定要区分就充分和必要. 所以, 我不是说用了 Paxos 就必须是消极的, 而是 Paxos 本身是消极的, 如果你用了 Paxos, 你自己要积极起来, 不能只依赖 Paxos. 例如, 你必须引入日志复制状态机模型, 避免全量拷贝. 另外, 你还必须引入超时重传机制, 主动积极拷贝. 为了优化, 避免读请求也要走完整的 Paxos 流程, 你还要引入 Leader. 天啊, 你在重新发明 Raft!

    Posted by ideawu at 2020-04-20 22:50:48 Tags: ,
|<<<1234>>>| 3/4 Pages, 24 Results.