通俗易懂关于Paxos的直观解释

京东云开发者
• 阅读 464

一、Paxos是什么

在分布式系统中保证多副本数据强一致性算法。

没有paxos的一堆机器, 叫做分布式

有paxos协同的一堆机器, 叫分布式系统

这个世界上只有一种一致性算法,那就是Paxos … - Google Chubby的作者Mike Burrows

其他一致性算法都可以看做Paxos在实现中的变体和扩展,比如raft。

二、先从复制算法说起

防止数据丢失,所以需要数据进行复制备份

2.1 主从异步复制



通俗易懂关于Paxos的直观解释



主节点接到写请求,主节点写本磁盘,主节点应答OK,主节点复制数据到从节点

如果数据在数据复制到从节点之前损坏,数据丢失。

2.2 主从同步复制



通俗易懂关于Paxos的直观解释



主节点接到写请求,主节点复制日志到所有从节点,从节点可能会阻塞,客户端一直等待应答,直到所有从节点返回

一个节点失联导致整个系统不可用,整个可用性的可用性比较低

2.3 主从半同步复制



通俗易懂关于Paxos的直观解释



主接到写请求,主复制日志到从库,从库可能阻塞,如果1~N个从库返回OK,客户端返回OK

可靠性和可用性得到了保障,但是可能任何从库都没有完整数据

2.4 多数派写读

往一个主接节点写入貌似都会出现问题,那我们尝试一下往多个节点写入,舍弃主节点。

通俗易懂关于Paxos的直观解释



客户端写入 W >= N / 2 + 1个节点, 读需要 W + R > N, R >= N / 2 + 1,可以容忍 (N - 1)/ 2 个节点损坏

最后一次写入覆盖先前写入,需要一个全局有序时间戳。

多数派写可能会出现什么问题?怎么解决这些问题呢?

三、从多数派到Paxos的推导

假想一个存储系统,满足以下条件:

  1. 有三个存储节点 2. 使用多数派写策略 3. 假定只存储一个变量i 4. 变量i的每次更新对应多个版本,i1,i2, i3..... 5. 该存储系统支持三个命令: 1. get 命令,读取最新的变量i,对应多数派读 2. set 命令,设置下版本的变量i的值,直接对应的多数派写 3. inc 命令, 对变量i增加,也生成一个新版本,简单的事务型操作

3.1 inc的实现描述



通俗易懂关于Paxos的直观解释



  1. 从多数中读取变量i,i当前版本1

  2. 进行计算,i2 = i1 + n,i变更,版本+1

  3. 多数派写i2,写入i,当前版本2

  4. 获取i,最新版本是2

这种实现方式存在一下问题:

如果2个并发的客户端同时进行inc操作,必然会产生Y客户端覆盖X客户端的问题,从而产生数据更新丢失



通俗易懂关于Paxos的直观解释



假设X,Y两个客户端,X客户端执行命令inc 1,Y客户端执行inc 2,我们期望最终变量i会加3

但是实际上会出现并发冲突

  1. X客户端读取到变量i版本1的值是2

  2. 同时客户端Y读取到变量i版本1的值也是2

  3. X客户端执行i1 + 1 = 3,Y客户端执行i1 + 2 = 4

  4. X执行多数派写,变量i版本2的值是2,进行写入(假定X客户端先执行)

  5. Y执行多数派写,变量i版本2的值是4,进行写入(如果Y成功,会把X写入的值覆盖掉)

所以Y写入操作必须失败,不能让X写入的值丢失 但是该怎么去做呢?

3.2 解决多数派写入冲突

我们发现,客户端X,Y写入的都是变量i的版本2,那我们是不是可以增加一个约束:

整个系统对变量i的某个版本,只能有一次写入成功。

也就是说,一个值(一个变量的一个版本)被确定(客户端接到OK后)就不允许被修改了。

怎么确定一个值被写入了呢?在X或者Y写之前先做一次多数派读,以便确认是否有其他客户端进程在写了,如果有,则放弃。

通俗易懂关于Paxos的直观解释



客户端X在执行多数派写之前,先执行一个多数派读,在要写入的节点上标识一下客户端X准备进行写入,这样其他客户端执行的时候看到有X进行写入就要放弃。

但是忽略了一个问题,就是客户端Y写之前也会执行多数派读,那么就会演变成X,Y都执行多数派读的时候当时没有客户端在写,然后在相应节点打上自己要写的标识,这样也会出现数据覆盖。



通俗易懂关于Paxos的直观解释



3.3 逐步发现的真相

既然让客户端自己标识会出现数据丢失问题,那我们可以让节点来记住最后一个做过写前读取的进程,并且只允许最后一个完成写前读取的进程进行后续写入,拒绝之前做过写前读取进行的写入权限。



通俗易懂关于Paxos的直观解释



X,Y同时进行写前读取的时候,节点记录最后执行一个执行的客户端,然后只允许最后一个客户端进行写入操作。

使用这个策略变量i的每个版本可以被安全的存储。

然后Leslie Lamport写了一篇论文,并且获得了图灵奖。

四、重新描述一下Paxos的过程(Classic Paxos

使用2轮RPC来确定一个值,一个值确定之后不能被修改,算法中角色描述:

•Proposer 客户端

•Acceptor 可以理解为存储节点

•Quorum 在99%的场景里都是指多数派, 也就是半数以上的Acceptor

•Round 用来标识一次paxos算法实例, 每个round是2次多数派读写: 算法描述里分别用phase-1和phase-2标识. 同时为了简单和明确, 算法中也规定了每个Proposer都必须生成全局单调递增的round, 这样round既能用来区分先后也能用来区分不同的Proposer(客户端).

4.1 Proposer请求使用的请求

// 阶段一 请求
class RequestPhase1 {
    int rnd; // 递增的全局唯一的编号,可以区分Proposer
}
// 阶段二 请求
class RequestPhase2 {
     int rnd;    // 递增的全局唯一的编号,可以区分Proposer
     Object v;   // 要写入的值
 }

4.2 Acceptor 存储使用的应答

// 阶段一 应答 
class ResponsePhase1 { 
    int last_rnd; // Acceptor 记住的最后一次写前读取的Proposer,以此来决定那个Proposer可以写入
    Object v;     // 最后被写入的值
    int vrnd;     // 跟v是一对,记录了在哪个rnd中v被写入了
}
// 阶段二 应答
class ResponsePhase2 { 
    boolean ok;
}

4.3 步骤描述

阶段一



通俗易懂关于Paxos的直观解释



当Acceptor收到phase-1的请求时:

● 如果请求中rnd比Acceptor的last_rnd小,则拒绝请求

● 将请求中的rnd保存到本地的last_rnd. 从此这个Acceptor只接受带有这个last_rnd的phase-2请求。

● 返回应答,带上自己之前的last_rnd和之前已接受的v.

当Proposer收到Acceptor发回的应答:

● 如果应答中的last_rnd大于发出的rnd: 退出.

● 从所有应答中选择vrnd最大的v: 不能改变(可能)已经确定的值,需要把其他节点进行一次补偿

● 如果所有应答的v都是空或者所有节点返回v和vrnd是一致的,可以选择自己要写入v.

● 如果应答不够多数派,退出

阶段二:



通俗易懂关于Paxos的直观解释



Proposer:

发送phase-2,带上rnd和上一步决定的v

Acceptor:

● 拒绝rnd不等于Acceptor的last_rnd的请求

● 将phase-2请求中的v写入本地,记此v为‘已接受的值’

● last_rnd==rnd 保证没有其他Proposer在此过程中写入 过其他值

4.4 例子

无冲突:



通俗易懂关于Paxos的直观解释



有冲突的情况,不会改变写入的值



通俗易懂关于Paxos的直观解释



客户端X写入失败,因此重新进行2轮PRC进行重新写入,相当于做了一次补偿,从而使系统最终一致

通俗易懂关于Paxos的直观解释



五、问题及改进

活锁(Livelock): 多个Proposer并发对1个值运行paxos的时候,可能会互 相覆盖对方的rnd,然后提升自己的rnd再次尝试,然后再次产生冲突,一直无法完成

然后后续演化各种优化:

multi-paxos:用一次rpc为多个paxos实例运行phase-1

fast-paxos 增加quorum的数量来达到一次rpc就能达成一致的目的. 如果fast-paxos没能在一次rpc达成一致, 则要退化到classic paxos.

raft: leader, term,index等等..

六、参考

  1. 文章主要来自博客:https://blog.openacid.com/algo/paxos/

  2. 一个基于Paxos的KV存储的实现:https://github.com/openacid/paxoskv

  3. Paxos论文:https://lamport.azurewebsites.net/pubs/paxos-simple.pdf

点赞
收藏
评论区
推荐文章
zhenghaoz zhenghaoz
4年前
分布式系统基石:Paxos
Thereisonlyoneconsensusprotocol,andthat'sPaxos.AllotherapproachesarejustbrokenversionsofPaxos.这个世界上只有一种一致性算法,那就是Paxos,其他共识算法只是Paxos的残缺版本。——MikeBurrows(Googl
架构师日记-为什么数据一致性那么难
在现代大型分布式软件系统中,有一个绕不过去的课题,那就是如何保证系统的数据一致性。著名的Paxos算法(Megastore、Spanner),Raft协议(ETCD、TiKV、Consul),ZAB协议(ZooKeeper)等分布式一致性解决方案,都是在此背景下而诞生的。
Wesley13 Wesley13
3年前
BFT等5种主流区块链共识的开源实现
共识算法是实现自主产权区块链的必不可少的关键环节,本文列出社区中相对成熟的区块链共识算法开源实现,包括BFT共识、Raft共识、Paxos共识、PoW共识等,可供希望开发自主产权区块链的团队参考学习。相关推荐:区块链开发系列教程(https://www.oschina.net/action/GoToLink?urlhttp%3A%2F%2Fww
Stella981 Stella981
3年前
Raft 算法在分布式存储系统 Curve 中的实践
作为网易数帆开源的高性能、高可用、高可靠的新一代分布式存储系统,Curve对于多副本数据同步、负载均衡、容灾恢复方面都有较高的要求。网易数帆存储团队选用Raft算法作为Curve底层一致性协议,并基于Raft的特性,实现了异常情况下的数据迁移和自动恢复。本文首先简要介绍一下Raft算法的一些基本概念和术语,再详细介绍其在Curve中的实践。Raft一致性
Easter79 Easter79
3年前
TiKV 源码解析系列文章(十七)raftstore 概览
第一作者:李建俊,第二作者:杨哲轩,王聪TiKV作为一个分布式KV数据库,使用Raft算法来提供强一致性。Raft算法提供了单一group的一致性,但是单一group无法扩展和均衡。因此,TiKV采用了MultiRaft的方式基于Raft算法提供能兼顾一致性、扩展均衡的KV储存。下文以3.0版本代码为例,讲述raf
Stella981 Stella981
3年前
Raft 与 Paxos的区别
RaftRaft概述        Raft一致性算法用于保证在分布式的条件下,所有的节点可以执行相同的命令序列,并达到一致的状态。这类的问题可以归结为“Replicatedstatemachines”问题。!关于Raft一致性协议的概要(http://static.oschina.net/uploads/img/
Wesley13 Wesley13
3年前
Java之一致性hash算法原理及实现
一致性哈希算法是分布式系统中常用的算法。比如,一个分布式的存储系统,要将数据存储到具体的节点上,如果采用普通的hash方法,将数据映射到具体的节点上,如key%N,key是数据的key,N是机器节点数,如果有一个机器加入或退出这个集群,则所有的数据映射都无效了。一致性哈希算法,解决了普通余数Hash算法伸缩性差的问题,可以保证在上线、下线服务
Wesley13 Wesley13
3年前
2、初探 ZooKeeper 技术内幕
分布式一致性“分布式”是大型系统实现高性能、高可用所常用的架构手段,本章节将概述“分布式一致性”的基本内容,以作为ZAB算法阐述的基础。分布式一致性的基本概念数据库系统的基础理论中,“事务”必须符合ACID,即为:Atomicity原子性、Consistency一致性、Isolation隔离性、Durabilit
基于Raft算法的DLedger-Library分析 | 京东物流技术团队
在分布式系统应用中,高可用、一致性是经常面临的问题,针对不同的应用场景,我们会选择不同的架构方式,比如masterslave、基于ZooKeeper选主。随着时间的推移,出现了基于Raft算法自动选主的方式,Raft是在Paxos的基础上,做了一些简化和限制,比如增加了日志必须是连续的,只支持领导者、跟随者和候选人三种状态,在理解和算法实现上都相对容易许多。
linbojue linbojue
9个月前
Etcd:分布式键值存储和配置系统
什么是Etcd?Etcd是一个开源的、分布式的键值存储和配置系统,由CoreOS团队开发并维护。它基于Raft一致性算法,用于存储和检索关键数据,并提供了高可用性、强一致性和高性能的特性。Etcd的设计目标是为分布式系统提供共享配置、服务发现、分布式锁和协