基于Paxos协议的数据同步与传统主备方式最大的区别在与Paxos只需任意超过半数的副本在线且相互通信正常,就可以保证服务的持续可用,且数据不丢失。

Basic paxos协议更新日志

我们将数据持久化的需求抽象为:在N个server的机群上,持久化数据库或者文件系统的操作日志,并且为每条日志分配连续递增的logID,我们允许多个客户端并发的向机群内的任意机器发送日志同步请求。

将每条日志的持久化流程都看作一个“Paxos Instance”,不同的logID代表不同的Paxos Instance形成的“决议(decision)”。即每一个logID标识着一轮完整paxos协议流程的执行,最后形成decision。机群内的每个server同时作为paxos的acceptor和proposer。

获取LogID

Server收到客户端的持久化日志请求后,因此向所有acceptor查询它们本地目前已写盘的最大logID,而只需收集到majority返回的结果,并选择其中最大的logID+1作为本次待持久化日志的logID。

从上面的描述可以看出,这里并不能保证并发提交的两条日志一定被分配到不同的logID,而是依靠后续的paxos协议流程来达到对一个logID形成唯一的decision的目的。

产生ProposalID

这里我们使用server的timestamp联合ip作为proposalID,其中timestamp在高位,ip在低位。(为了唯一卫衣proposalID,只要要求时钟的误差范围小于server重启的时间)

Prepare阶段

只有在这个Paxos Instance内(即针对这个logID)没有response过proposalID大于等于当前proposal的,并且也没有“接受(accept)”过proposalID大于当前proposal的,才可以response,并承诺不再accept那些proposalID小于当前proposal的。

上面Prepare阶段的处理流程暗示,对于分配到相同logID的不同日志,由于他们的proposalID不同,acceptor在response一个较小proposalID后,是允许继续response后来的较大的proposalID的。

Accept请求阶段

如果majority的response中的日志内容都为空,那么可以向所有acceptor发出accept request并携带上当前日志内容;而如果有任意的response中的日志内容有效,那么说明当前logID已经别其他日志占用,因此需要回退,回到第一步“获取logID”重新执行。

Accept执行阶段

而如果ProposalID小于当前最大minProposal的,则说明有logID切proposalID更大的proposal在并发执行,当前proposal会被覆盖,因此回复proposer要求回退到第一步“获取logID”重新执行。

这里的流程暗示,针对一个logID,如果之前已经有日志内容持久化成功,那么这条日志一定会被选为accept request;而如果之前日志内容仅仅在小于半数的server上写到磁盘,那么最终这条logID的内容有可能是有效日志,也有可能内容为空。(本例中可能两个提议者竞争一个logID,然后本例中为了加快收敛,其中一个自动放弃,选择更大的logID)

日志内容读取

在读取的时候也需要回放一遍paxos协议,防止读到未提交的日志(相当于raft的提交阶段?)。

为什么需要comfirm日志

假设有a, b, c 3个节点, 刚开始的时候, 3个节点的值是一致的, 都是v1. 这时候一个proposer发起一轮paxos请求, 希望将值设置成v2. 那么可能出现两种情况, 一种是paxos成功了, a, b, c的多数派中value设置成了v2, 也就是说v2被批准了(假设a, b, c的值分别是v1, v2, v2). 另外一种是失败了, a, b, c的多数派中value仍然是v1, 也就是说v2没有被批准, 但是a, b, c中可能存在少数派节点的值是v2(假设a, b, c的值分别是v1, v1, v2). 按照p2a的流程, 选取proposal的原则是选取多数派中, proposal号最大的作为期望被批准的value. 在上述两种情况下, 当reader读取数据时, 按照p2a的逻辑, 既可能读到v1, 也可能读到v2, 那这样的话, 如果有一个acceptor故障了, 其实reader就无法准确知道之前被批准的value了吧? 我看paxos made simple的论文中也提到, 如果有一个acceptor故障了, 也不可能找出被批准的value了, 唯一的办法就是issue a proposal, 然后运行一轮新的paxos算法了。这是不是说, 不管reader读到的是v1还是v2, 仍然发起一轮paxos协议, 保证一致性就ok了

简而言之,就是只有leader某条日志才知道某条日志复制到大多数,而参与者并不知道。所以在提交的时候,要么再跑一次paxos,要么写一条comfirm日志

Multi-paxos更新日志

Leader的产生

通过Multi-paxos,我们可以简化掉产生logID阶段和prepare阶段,而是由唯一的leader产生logID,然后直接执行accept,得到多数派确认即表示redolog同步成功。

首先,需要明确的是Multi-Paxos协议并不假设全局必须只能有唯一的leader来生成日志,它允许有多个“自认为是leader的server”来并发生成日志,这样的场景即退化为Basic-Paxos。

Leader产生并不是走完一个完整的basic paxos流程,而是只走了proposal prepare阶段,所以可能同时产生多个leader。因此,我们需要先写一条StartWorking日志,成功写下这条日志升级为唯一leader

confirm日志优化

因为在读取日志的时候,需要再进行一次basic paxos协议。因此引入confirm日志,针对刚刚提交的日志写入一条confirm日志。出于性能考虑,实际操作中,批量发送confirm日志,因此,读到未确认的日志,还是需要走一遍paxos协议

新任leader对日志的重确认

新任leader可能保存的日志落后于前任leader,因此需要对小于当前集群中max log id的日志都进行,只要大多数回复max log id即可。这样的结果可能超越了前任已经accept的日志记录,称为最大commit原则。

“幽灵复现”日志的处理

使用Paxos协议处理日志的备份与恢复,可以保证确认形成多数派的日志不丢失,但是无法避免一种被称为“幽灵复现”的现象,如下图所示:

x Leader A B C
第一轮 A 1-10 1-5 1-5
第二轮 B 宕机 1-6,20 1-6,20
第三轮 A 1-20 1-20 1-20
  1. 第一轮中A被选为Leader,写下了1-10号日志,其中1-5号日志形成了多数派,并且已给客户端应答,而对于6-10号日志,客户端超时未能得到应答。

  2. 第二轮,A宕机,B被选为Leader,由于B和C的最大的logID都是5,因此B不会去重确认6-10号日志,而是从6开始写新的日志,此时如果客户端来查询的话,是查询不到6-10号日志内容的,此后第二轮又写入了6-20号日志,但是只有6号和20号日志在多数派上持久化成功。

  3. 第三轮,A又被选为Leader,从多数派中可以得到最大logID为20,因此要将7-20号日志执行重确认,其中就包括了A上的7-10号日志,之后客户端再来查询的话,会发现上次查询不到的7-10号日志又像幽灵一样重新出现了。

对于将Paxos协议应用在数据库日志同步场景的情况,“幽灵复现”问题是不可接受,一个简单的例子就是转账场景,用户转账时如果返回结果超时,那么往往会查询一下转账是否成功,来决定是否重试一下。如果第一次查询转账结果时,发现未生效而重试,而转账事务日志作为幽灵复现日志重新出现的话,就造成了用户重复转账。

为了处理“幽灵复现”问题,我们在每条日志的内容中保存一个generateID,leader在生成这条日志时以当前的leader ProposalID作为generateID。按logID顺序回放日志时,因为leader在开始服务之前一定会写一条StartWorking日志(先同步再写StarWorking日志),所以如果出现generateID相对前一条日志变小的情况,说明这是一条“幽灵复现”日志(它的generateID会小于StartWorking日志),要忽略掉这条日志。


其中,log1和log6分别是第一轮和第二轮的start working log。

本质上是通过在发生leader权抢占的时候,ProposalID的递增性质保证了不会回放以前的logID。

multi-paxos和raft对比

以下是我自己的观点,不保证正确性

  1. raft logID递增,multi_paxos并不需要
  2. multi_paxos为了保证读到的日志已经提交,需要再走一边paxos确认日志;raft每次获得多数派的认可后,也需要向参与者发送提交命令(相当于读取命令)。
  3. 因为multi-paxos的logID不连续,在leader权抢占的时候,所以有了最大commit原则,最大commit原则可能产生幽灵日志,如果epochID小于上一条,就不回放,所以有了不回放上一个任期的日志。

幽灵日志解释很清晰

paxos协议更新日志的更多相关文章

  1. Paxos协议理解

    第三次报告: 理解Paxos协议 一. Paxos协议背景 什么是Paxos协议? 一般地,从客户端和服务器的角度,任何一个分布式系统都可以理解成由一个服务器集合和一个客户端集合组成,一个或多个客户端 ...

  2. Easy Sysprep更新日志-skyfree大神

    Easy Sysprep更新日志: Skyfree 发表于 2016-1-22 13:55:55 https://www.itsk.com/forum.php?mod=viewthread&t ...

  3. Paxos协议超级详细解释+简单实例

    转载自:  https://blog.csdn.net/cnh294141800/article/details/53768464 Paxos协议超级详细解释+简单实例   Basic-Paxos算法 ...

  4. Paxos协议笔记

    对Paxos协议的介绍,可以通过Leslie Lamport的<Paxos Made Simple>展开学习和了解.Paxos算法在允许失败的分布式系统环境下,实现系统一致性.失败的情况有 ...

  5. [实战]MVC5+EF6+MySql企业网盘实战(29)——更新日志

    摘要 NetDisk更新日志,及项目使用说明. 开发工具 Vs2013+mysql+ef6+mvc5 bug 1.在加载列表的时候,默认加载的所有,修改为,过滤逻辑删除的文件. 2.加载音乐,文档等分 ...

  6. AgileEAS.NET SOA中间件平台更新日志 2015-04-28

    一.前言 AgileEAS.NET SOA 中间件平台是一款基于基于敏捷并行开发思想和Microsoft .Net构件(组件)开发技术而构建的一个快速开发应用平台.用于帮助中小型软件企业建立一条适合市 ...

  7. python解析git log后生成页面显示git更新日志信息

    使用git log可以查到git上项目的更新日志. 如下两个git项目,我想把git的日志信息解析成一个便于在浏览器上查看的页面. https://github.com/gityf/lua https ...

  8. 更新日志 - fir.im「高级统计」功能上线

    距离 2016 年到来只剩 10 个日夜,fir.im 也准备了一些新鲜的东西,比如「高级统计」功能和「跳转应用商店」功能,帮助你更好地管理.优化应用,欢迎大家试用反馈:) 新增高级统计功能 这次更新 ...

  9. 更新日志 - fir.im Jenkins & Gradle 插件上线

    最近 fir.im 工程师们效率爆表,fir.im 实用工具集合又添加了新的成员-- Jenkins & Gradle 插件,让 App 打包上传更加简单快速. fir.im Jenkins ...

随机推荐

  1. genymotion无法连接相机问题

    genymotion模拟器即时打开了相机的开关,也无法连接到相机.这是因为其他进程占用了相机,虚拟设备无法获得,可以尝试: 1.不关闭模拟器,重启adt的Eclipse 2.重启ADB,adb kil ...

  2. POJ 2462 / HDU 1154 Cutting a Polygon

    就这样莫名其妙的过了,不过可以确定之前都是被精度卡死了.真心受不了精度问题了. 题意:一条直线在一个不规则多边形内的长度,包括边重合部分. 首先计算出所有交点,然后按想x,y的大小进行二级排序. 然后 ...

  3. Oracle EBS标准错误信息如何追踪 (Debug)

    http://www.cnblogs.com/songdavid/articles/2067534.html 调用EBS标准API的时候,可能会返回一些让人看不懂的错误,比如最近我在开发rcv_tra ...

  4. Android-Java-了解编译

    在多年以前,计算机早期的运行方式是 01010100 ....., 这种方式的时候,开发人员编写代码是把01010101/01100101/01010111 ..... /来汇编写代码,这种方式特别痛 ...

  5. 项目笔记---WPF之Metro风格UI

    写在前面 作为新年开篇的文章,当然要选择比较“Cool”的东西来分享,这自然落到了WPF身上,WPF技术自身可塑性非常强,其强大的绘图技术以及XAML技术比WinForm而言有本质的飞跃. 切入正题, ...

  6. .net core获取服务器本地IP及Request访问端口

    string str = (Request.HttpContext.Connection.LocalIpAddress.MapToIPv4().ToString() + ":" + ...

  7. C# Lock锁(个人随记)

    先看看为什么要用锁 需求:多线程处理值的加减   static int NoLockData = 0; public static void NoLockNormalTest(int threadIn ...

  8. HTML、CSS

    表格标签: 表格标签有:<table> <tr> <th> <td> 让内容居中的标签:<center> 按钮标签:<button&g ...

  9. 【bug记录】jpa 解决org.hibernate.lazyinitializationexception could not initialize proxy - no session

    前言 最近开发项目比较忙,Spring Cloud的笔记得稍稍放放了,下午出来个bug,恶心的不行,功能很简单,也没有什么级联或复杂的映射关系,就是一直在报三个异常 Caused by: com.fa ...

  10. Mac 切换到行首和行末的方法

    苹果笔记本没有home键和end键 但是使用 command + 方向键左键可以回到行首, command + 方向键右键可以去到行末