• 2021-04-18

    再谈 Paxos 和 Raft

    Views: 221 | No Comments

    我之前写过一些谈 Paxos 的文章[1][2], 特别是将 Paxos[3] 和 Raft[4] 进行了对比. 由于我更多的是站在工程实现的角度考虑两种技术的优缺点, 所以造成了不少读者感受到我有非常强的"贬 Paxos, 赞 Raft"的倾向. 不可否认, 从工程实现的角度, Paxos 的指导意义非常抽象且不直接, 所以我们必须""亲 Raft 远 Paxos".

    实际上, 许多人认为 PaxosRaft 不是同一层面的东西, 不可同日而语. 另一方面, 某种角度看他们又同一层面的东西, 当然要做比较. 所以, 我们在讨论这两种技术时, 要注意所设的场景和条件, 否则极易让人误会.

    有一个说法比较经典:

    Mike Burrows, inventor of the Chubby service at Google, says that “there is only one consensus protocol, and that’s Paxos” - all other approaches are just broken versions of Paxos. - [source]

    翻译便是:

    Google Chubby 的发明者 Mike Burrows, 说过"世上只有一种共识协议, 那就是 Paxos" - 其它的全是 Paxos 的残缺版本. - [来源]

    这个说法常常引申之后用来对比 Paxos 和 Raft, 然后把 Raft 归结为所谓的"残缺版本", 以获得无知而且猥琐的心理满足感. 根据来源网址, 似乎 Mike 只说 Paxos 是唯一一种共识协议, 后面那句轻浮的, 狡黠的, 稚儿指点江山似的说法 - 其它的全是 Paxos 的残缺版本 - 估计是文章的作者加上的, 并非 Mike 本话.
    Continue reading »

    Posted by ideawu at 2021-04-18 11:59:48 Tags: ,
  • 2021-04-17

    面向全球的应用的系统架构

    Views: 97 | No Comments

    某些产品是面向全球用户的, 所以会在全球多个机房部署业务进程(Service)和数据库(Database). 这带来了所谓的数据一致性问题. 以用户加好友功能作为例子:

    用户 A 在中, 在 App 中向用户 B 发送了好友申请. 用户 B 在美国, 打开 App 刷新, 没有看到有任何未处理的好友申请…

    这是一个非常典型的例子. 我们仔细分析一下问题出在哪.
    Continue reading »

    Posted by ideawu at 2021-04-17 18:25:59
  • 2021-04-17

    并发编程的核心技术 – 多版本(Multi Version)

    Views: 281 | No Comments

    在单机编程时代, 每一项数据只有唯一的一份, 对数据的修改也是 in-place 的. 但是, 在并发编程领域, 包括分布式系统, 数据多版本(Multi Version, Versioning)是核心.

    我们先从单机编程的内存操作出发. 对于内存的操作, 都是原地(in-place)更新的. 对象和内存空间强绑定, 当更新对象时, 是将对象的内存空间擦除然后用新数据写覆盖. 到了多线程编程时代, 就引入了锁机制, 因为擦除和写操作过程不是原子性的, 可能擦除到一半时, 就被其它线程读取了, 因此要加锁.

    单机的硬盘操作, 基本也是借鉴内存操作, 也是对象和硬盘空间强绑定. 至少大部分程序员的思想是这样的, 这样比较直观. 跟内存操作一样, in-place 也遇到了操作的原子性挑战. 内存本来就是易失的(掉电后丢失), 但硬盘不一样, 数据需要持久化(掉电不丢失), 即使靠加锁解决了访问原子性问题, 但解决不了数据丢失问题. 所以, 硬盘操作是最先引入多版本技术的. 当需要修改某个对象时, 在另外的地方保存对象的新数据, 然后在另外的地方原子性地修改指向新数据的"指针". 事实上, 指针的修改也是多版本的, 不是 in-place 的, 后面会细说.
    Continue reading »

    Posted by ideawu at 18:20:34
  • 2021-04-16

    分布式系统中的先后顺序问题 – 逻辑时钟, 原子钟和停止等待

    Views: 216 | No Comments

    分布式系统中的一致性问题, 本质就是操作的先后顺序问题. 先后顺序, 纯朴的理解就是时间的先后, 也即时钟的先后. 众所周知, 时钟受许多因素影响, 例如观察者, 时钟源(钟表, 系统时间), 时钟同步等等, 单纯依赖时钟的读数来区分先后顺序, 会造成许多的问题.

    以银行转账为例子.

    在一个虚拟的银行系统中, 用户直接修改离自己最近的银行的数据库, 而数据库本身会自动地将修改同步到其它地点.

    中国的用户 A 在中国的数据库里修改了自己账户的余额, 扣减 100 元, 同时修改了用户 B 在中国的数据库里的余额, 加上 100 元.

    接着, 用户 A 私下告诉在美国的用户 B, 说我已经给你转账了 100 元. B 直接读取美国的数据库. 如果 B 发现自己的账户增加了 100 元, 那么我们就说, 两地的数据库组成的系统, 其行为符合强一致性.

    我们知道, 数据库同步有延迟, B 有可能第一次查看的时候没有发现转账. 我们如何控制 B 的行为, 保证他一定会看到这次转账呢?
    Continue reading »

    Posted by ideawu at 2021-04-16 21:47:25
  • 2021-04-09

    全球分布式数据库遇到的经典问题

    Views: 482 | 2 Comments

    全球分布式数据库因为地理距离较远(上万公里), 网络通信延迟一般在 100ms 级别, 所以只能采取异步复制的方案. 采取异步复制方案, 那就决定了最终数据被复制的时效性无法得到保证, 例如正常情况仅仅比网络延迟多几毫秒(100ms+). 但坏情况时, 例如, 因为网络线路不好, 数据可能要花费数秒甚至数分钟才能同步. 这就导致了非常恼人的用户体验.

    考虑这样的场景:

    某网络游戏平台的用户 A 在中国, 而用户 B 是他曾经的邻居, 目前在美国. 某日, 用户 A 将游戏中的道具转给了用户 B, A 在游戏中看到了明确的操作成功的提示, 而且刷新也确认道具已经转交.

    A 在私下用微信告知了这个操作, 然后让 B 在游戏中查看自己的道具背包.

    该游戏平台在中国有一个数据库, 在美国也有一个数据库, 两地的数据库是异步复制(最终一致性)的. 因为游戏平台自己的网络线路问题, 这个操作的数据一直没有同步到美国. 但是, 微信所使用的网络线路没有问题, B 收到了 A 的微信消息.

    B 在美国不断地刷新, 一直看不到道具, 这让他非常疑惑. 明明 A 说道具已经转交了呀, 而且还发截图了.

    这就是经典的异步复制(最终一致性)导致的问题. 要怎么解决呢? 有很多方案, 但是, 正如”没有银弹”一样, 每一种方案都有缺陷. 我们一种一种地分析.

    Continue reading »

    Posted by ideawu at 2021-04-09 22:03:57
  • 2020-06-21

    一个 Paxos 库的功能模块划分

    Views: 1821 | No Comments

    Paxos 算法本身非常狭窄, 只解决共识问题, 如果只在这个领域封装一个 libpaxos 没有必要, 没有太大的使用场景. 如果要封装一个 Paxos 库, 几乎就是要重新发明(实现) Raft. 但 "Paxos" 这个词比较唬门外汉, 所以咱们就说"开发一个 Paxos 库"吧.

    正如计算机领域解决问题的经典思路, 一个 Paxos 库应该拆分成三五个模块: 共识模块, 排序模块, 应用模块, 重传模块.

    共识模块: 使用本节点所知的下一个未使用的日志序号, 执行 Paxos 算法的两阶段. 如果一条日志(对应一个序号)达成共识, 将日志传递给日志排序模块. 这个模块要记录所有已达成共识的序号, 当然, 不一定要离散地记录每一个序号, 可以优化成记录离散的 range, 最终只有一个 range. 维护这些 range, leetcode 上有一些题目有涉及, 有成熟可用的代码.

    排序模块: 接受共识模块传递过来地已达成共识的日志, 按序号进行排序(因为是乱序达成共识). 然后连续地推进 commitIndex. 将 committed 的日志, 按连续的顺序发给日志应用模块.

    应用模块: 接受日志排序模块的输出, 用独立的线程将日志发送给状态机(apply). 状态机 Apply 完成每一条日志之后, 更新 appliedIndex.

    重传模块: 已经达成共识的日志, 不一定能全部被共识模块收到. 这时, 需要日志重传模块从其它节点拉取缺失的日志, 传递给日志排序模块.

    以上各个模块, 组成这样的工作链条:

         +--- 重传模块 --+
        /                \
    @--+----- 共识模块 ---- 排序模块 ---- 应用模块
    

    如果从一条日志的生命周期的角度分析, 这个工作链条代表着日志的生命周期.

    具体的实现编写代码的时候, 每一个模块肯定要对输入进行校验. 例如, 已经达成过共识的日志, 共识模块不再接受(leetcode 上面有判断一个整数是否落在 range 列表中的题). 小于等于 commmitIndex 的日志要被排序模块丢弃, 等等.

    还有优化方面的考虑, 特别是重传模块, 最好借鉴 TCP 流量控制算法. 本质上来说, 整个 Paxos 库其实是在实现并裁剪 TCP 协议. 这里 Paxos 库要用拉取重传, 如果是 Raft 的话, 是推送方(leader)主动重传.

    Posted by ideawu at 2020-06-21 11:11:39
|<<<1234>>>| 1/4 Pages, 24 Results.