You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
-
背景
部分用户在使用Pika的过程中,期望主从数据是一致的,如金融系统等,对一致性可靠性的要求较为强烈,因此经过商讨我们决定开发一致性版本的Pika服务。
下面我们先了解一下 PacificA 协议。
方案设计
Pika 目前使用的实例同步方式是主从复制的形式进行实例故障恢复的,从实例在初次链接主实例的时候会进行全量复制,增量复制是通过binlog进行传输的,Pika重点会出现数据不一致的情况是全量复制和增量复制的过程中。
全量复制:可能会在同步进行的过程中主实例挂了,导致主从复制全量复制没有进行完成,会出现主从数据不一致的现象。
增量复制:增量复制是依据主节点发送给从节点的binlog并进行apply到主节点中,而在同步的过程中,不保证主从实例都是存活的状态,具有不可预测性,假如发生以下的情况:
a. 主节点的实例down掉或者从节点的实例down掉后,需要进行断点续传,如果这个过程中有binlog未消费,或者binlog消费了,DB 没写成功的话,可能会出现主从节点不一致的情况。
解决方案
Pika的具体实现主从架构图下图所示:
主从同步的交互流程
以前的主从复制增量复制的流程:
主要过程
keepalive
master和slave端都有keepalive机制。都是在auxiliaryThread中周期性执行。
对master而言,它为每个slavenode分别记录了last_send_time和last_receive_time,当last_recv_time距离当前时间超过20s,将slave删掉。当last_send_time距离当前时间超过2s并且sent_offset == acked_offset,发送空的binlogsync请求给slave,作为心跳包。
对于slave而言,只有处于kWaitDBSync和kConnected状态的slot才需要进行keepalive,如果上一次收到master的数据包的时间距离当前已经超过了20s,将DB的状态重置为kTryConnect,重新走一遍流程。
数据同步
数据同步从slave节点收到slave命令开始,之后slave节点发送metasync到master,获取master的拓扑并进行比对。校验通过后初始化本地的SyncSlaveSlot,并发送TrySync给master。master校验slave发来的offset对应的binlog文件本地是否还持有,如果已经删除,改为执行全量同步。如果存在即为增量同步。接下来介绍增量复制的场景。
修改过后的流程
修改过后的点主要在于主节点写DB和给从节点同步binlog的时机,修改后的流程如下所示:
数据同步从slave节点收到slave命令开始,之后slave节点发送metasync到master,获取master的拓扑并进行比对。校验通过后初始化本地的SyncSlaveSlot,并发送TrySync给master。master校验slave发来的offset对应的binlog文件本地是否还持有,如果已经删除,改为执行全量同步。如果存在即为增量同步。接下来介绍新的增量复制的场景。
待Slave写完binlog后,主节点收到了所有的从节点写完ACK的消息的时候,去通知所有的主节点和从节点同时去写DB,这样可以保证主从节点数据的一致性。
如下图所示:
(插入时序图)
故障恢复及故障处理方案
全量复制过程中主节点故障:全量复制的过程中,如果主节点故障,从节点没有做完全量复制的情况下是不允许做主从切换的。
全量复制过程中从节点故障:从节点故障的话,只需要链接上继续全量复制,断点续传即可。
增量复制过程中主节点故障:增量复制过程中如果主节点故障的话,对应的binlog没有传输过去,没有收到从节点返回信息,不能进行写 DB,那对应的数据就没有落到磁盘上,用户也不会读取到,不存在主从数据不一致的现象。
增量复制过程中从节点故障:增量复制过程中,如果从节点故障的话,那么会出现主节点传输给从节点的binlog没有真正的传输给从或者没有将ACK返回给从,不存在数据不一致的问题。
codis-sentinel部分的改造
codis-sentinel主要是负责进行 Pika服务主节点和从节点之间的切换的,当主节点挂掉之后,会判断当前是在做增量复制还是全量复制,如果是全量复制没做完的话不对外提供服务,但是这里会有个问题(如果主从不是第一次建立连接,因为网络断联等原因导致的offset 偏移量差距过大的情况下),如果不允许进行主从切换,是否会影响用户的服务?或者是如果主库不可用hang住了,此时全量复制没完成,也会影响业务?
解决方案
这里还是需要等待数据完整后再做切换,否则切换后会有数据丢失。
代码修改点整理
全量复制点:不需要做更改。全量复制是在初步建立实例的时候完成的,这个时候不知道主从分别什么时间点建立链接,另外全量复制时,写入量太大了,如果一直等待ACK回包会导致阻塞,或者是写入过慢,综合不改全量复制。
主实例部分:修改写DB的时机,目前是用户的请求来了就写DB,在写binlog,但是这样的话会存在部分数据,写到了DB中,服务挂了,没有来得及写binlog,会导致主从数据不一致。
考虑到这些问题,我们本次方案设计中将写DB的步骤放在binlog,等待从节点写binlog成功的ACK消息后,主节点和从节点一起写DB,具体代码点:
从实例部分:从实例部分,是需要将所有的ACK返回给主实例之后,主实例和从实例同时写DB,保证主从数据的一致,但是这里可能会出现一个问题。
此时考虑一个case:可能会出现主实例没有写成功从实例成功了,或者是主实例成功了从实例没成功,那么这种场景怎么处理呢?是否需要做数据回滚等操作呢?
目前从实例写DB和binlog是异步的,现在的逻辑是写完binlog之后再写DB,目前需要修改的是将写完binlog改为收到主实例写完DB的回包?还是不考虑写失败问题?
削弱强一致性
有时候强一致性不是必要的,并且削弱一致性会带来一些性能上的提升。
强一致性有如下两个要求:
削弱第一条将会导致不同replica之间的state diversion,这个处理起来非常麻烦。所以我们主要从第二点入手。这里主要有两种方式来实现:
第一种就是去掉租期。去掉租期的话,将会导致Primary Invariant无法保证,也就是说,可能会在某个时间,replica group中有两个primary。当查询请求发往old primary时,将有可能访问旧的数据。但是无需担心state diversion的问题,因为new primary肯定是先前配置中replica group中的一员,这样当old primary接收到update请求,若要提交必须所有secondary prepared。当发往new primary之后,他知道自己是new primary,将不会接受该prepare,导致update失败。
第二种便是secondary也参与处理读请求。发往secondary的请求有可能读到过期的数据。
确认从写成功主失败怎么做
失败后进行3次重试(确认业务方会重试的),重试不成功后kill掉主实例。
主从实例之间定期进行副本同步,剔除不健康的副本(Pegasus这么做的),这个操作由metaserver数据管理节点去进行,但是对于Pika来说改动太大,不采纳。
Pika写rocksdb失败的场景较少,承诺弱一致性,不承诺强一致性,进行多次重试,成功后,不kill掉主实例,继续接受业务方的请求。
确认主成功从失败怎么做
重试3次(确认业务方会重试的),继续下面key的写请求,我们对用户强调非强一致性。
讨论结果
无论主从失败retry,主从写失败都进行自杀。
关于写DB的顺序
对于Pika来说,主节点写DB是顺序写入的,而从节点在数据apply binlog的时候目前是异步顺序的,因为加的是key锁,所以是同一个key的多个binlog在apply的时候能保持顺序,但是不同的key可能会出现顺序不一致的场景。
例子
目前Pika的实现可能会出现这种情况,但是我们暂时不考虑这些,只保证最终一致性,不保证强一致性,牺牲部分的瞬时一致性。
关于数据的写入幂等性
幂等性的保证还是按照原来的逻辑,普通的写入操作如set这种是幂等的,重复执行就是覆盖,但是类似incr这种我们是不保证幂等性的。
case举例
主实例写失败,从实例成功,我们所做的操作是将主实例kill掉,从实例提升为主。
此时对于set等类型的操作指令来说,是幂等的,可以保证客户端在失败重试时数据一致。
但是对于诸如incr,decr等需要进行数据计算的操作来说,重复操作可能会导致客户端返回的数据与正确的数据不一致。
参考文档
参考文档链接
Beta Was this translation helpful? Give feedback.
All reactions