redis双主设计

redis双主设计

背景

目前redis仅支持主从复制模式,能够支持在线备份、读写分离等功能,实际应用中一般经过sentinel服务作主从切换的管理,这增长了管理的复杂度和维护成本,基于此360基础架构组联合DBA从redis内部实现了双主功能。 redis

主从复制介绍

redis支持树形的主从异步复制,并具备非阻塞、部分同步等特性,下面简单介绍下其实现原理以及目前redis主从复制模式在故障发生时的数据丢失状况。 缓存

实现原理

数据同步

slave开始链接到master时须要同步master已有的数据,具体过程以下图所示,主要有如下几个步骤: 网络

  1. slave向master发送PSYNC命令,将缓存的master的runid和reploff发送给master。
  2. master 根据slave发送的reploff判断须要开始同步的数据是否在当前缓冲区(积压空间)中,若是在的话完成和slave的链接创建并向slave发送 CONTINUE回复标志,开始发送积压空间中的数据;不然,向slave发送FULLSYNC标志并开启子进程dump当前时刻的快照数据到本地rdb 文件。
  3. slave接收psync命令的返回值,并判断是CONTINUE仍是FULLSYNC,若是是CONTINUE则完成链接过程准备接收master数据;不然,触发接收master dump的rdb文件事件,等待master发送rdb文件。
  4. master将快照数据dump到本地文件后,向slave发送rdb文件
  5. slave接收完master发送的rdb文件后,清空本地库并从新加载接收的rdb文件,因为这时的数据变化较大,若是开启了aof选项则还需进行aof rewrite操做
  6. 开始传播master积压空间中的新数据。

数据传播

master 实例会维护其slave实例列表,当有更改操做发生时,其会经过链接创建时建立的socket向全部slave实例发送操做命令进行数据传播,同时为了防 止故障恢复slave从新链接master时每次都进行全量同步,master实例会内部维护一个缓冲区(积压空间)来缓存部分slave命 令,master数据传播的具体过程为: 架构

  1. 写入本地库
  2. 写入积压空间
  3. 触发写入slave实例事件,进行异步传播;若是此刻正在进行数据同步操做则将命令写入缓冲区,待同步操做完成后再触发向slave实例写入事件。

数据丢失

redis 主从模式并不能保证数据的100%完整,在网络故障、主库宕机等状况下提高slave为master时可能会丢失部分数据,以下图所示,假如在某个时刻 master的数据还未彻底传播给slave时,master宕机等状况发生并将slave提高为master,这时原来master未传播的一部分数据 将丢失。 异步

双主复制设计

前提假设

因为时间、精力等因素,目前咱们在进行双主设计时结合以下实际项目需求进行了一些设计折衷,后续咱们会继续完善相关设计。 socket

  • 上层保证某个时间点只有一个master在写 性能

  • 故障时容许丢失少许数据 spa

整体设计

为 避免额外维护成本,双主模块彻底在redis内部实现,双主两个实例各自建立一个socket进行彼此通讯;加入双主复制后不会影响原有的主从复制模式, 但以下图所示,主从复制实例能够经过咱们新增的doublemasterof命令转化为双主复制,双主复制实例也能够经过原有的slaveof命令转换为 主从复制实例。 设计

同步策略

redis 双主实例网络故障恢复或重启等状况下会进行从新链接以同步彼此数据,主从模式下同步策略很简单,只须要从库同步主库数据便可,而双主模式下咱们必须根据一 定的策略来选出一个实例做为数据同步的对象,咱们考虑到两种具体同步策略:基于数据量和基于时间戳的同步策略。 对象

数据量同步策略

基 于数据量的同步策略能够理解为数据量少的实例去同步数据量多的实例,这种同步策略在故障发生时数据已经所有传播到另一个实例的状况下,故障恢复后能够保 证数据完整性,但以下图所示,假如原来操做在双主A上执行,某一时刻双主A上的操做还未同步到双主B上发生了网络故障,上层会切到B上继续写入,当写入了 图中红色所示大小的数据后网络故障恢复,这时进行数据同步时就有可能丢失A或B上的部分数据。

时间戳同步策略

对 于基于时间戳的同步策略,咱们会在redis内部维护双主实例的最近更新时间戳,故障恢复进行数据同步时时间戳较旧的实例会同步时间戳较新的实例;和基于 数据量同步策略同样当故障发生时若是数据已经所有传播到另外实例则故障恢复后能够保证数据完整性,不然,以下图所示故障恢复后将丢失双主A实例的部分数 据。

咱们的选择

根据业务需求,咱们须要保证最新写入的数据不会丢失,因此具体实现上咱们选择了基于时间戳的同步策略。

同步实现

咱们在redis原有的全同步,部分同步的基础上增长了ignore resync策略以实现双主同步,具体实现以下图所示,有如下几个步骤:

  1. 双主实例A连接到另一个实例B时会经过发送2mastersync命令,该命令会将A实例最后的更新时间戳发送给B
  2. B 实例判断A的最后更新时间戳是否比本身新,若是比本身新则直接将A标示为ONLINE并向A发送IGNORE runid reploff信息,A接收到IGNORE信息后记录下runid reploff,便可完成与B的同步;不然,则按照主从同步方式进行全同步或部分同步。

数据传播

对 一个双主实例的更改操做,redis内部会经过双主实例创建链接时建立的socket异步传播给另外一个双主实例,这里要解决的问题是要避免数据再次传播回 来,具体实现上咱们经过双主实例的runid进行判断,每一个双主实例内部会维护其另一个实例的runid信息,当有更改命令要执行时,咱们会经过 runid来判断该命令是不是其双主实例传播过来的,若是是将再也不回传。

复制偏移量维护

redis主从实现机制上,当通讯模块接收到主库的更改命令时会直接在从库上增长其复制偏移量来记录数据同步的位置,而对于双主实例咱们知道为避免数据循环传播,双主实例A传播给双主实例B的命令不会回传过来,那么该如何维护其复制偏移量呢?设计上咱们考虑了两种策略:

策略一

以下图所示,双主A向双主B传播一条数据后,B会回复A一个ACK length确认,A接收到确认信息后将本身的复制偏移量增长length。

策略二

以下图所示,双主A再向B传播数据以前本身主动增长复制偏移量,双主B不会向双主A回复确认信息

Imgur

策略一对比策略二进一步保证了数据的完整性,但同时带来了必定的网络开销,两种策略都不能彻底保证复制偏移量再网路故障下的正确性(策略一在ACK丢失的状况下没法保证复制偏移量正确),结合目前的需求为了避免影响性能咱们选择了策略二。

小结

结合目前项目的需求咱们在redis内部实现了双主功能,可是也有一些须要改进的地方,欢迎你们提出意见,后面咱们会不断完善,敬请关注!


http://weibo.com/p/1001603872517453663800

相关文章
相关标签/搜索