写在前面

redis读写分离解决死锁 redis读写分离实现_RDB


本文一起看下redis的读写分离架构。

1:为什么要读写分离

读写分离,即主库执行写请求,然后写的数据同步到从库,从库执行读请求,架构图如下:

redis读写分离解决死锁 redis读写分离实现_RDB_02

一般读写分离带给我们的好处可能如下:

分担主库的读压力,增加系统处理请求的能力。
数据备份。

2:首次同步的过程分析

假设我们有2个redis实例,IP192.168.10.110端口6379,以及IP192.168.10.111端口6379,需要设置111为110的从库,可以执行slaveof 192.168.10.110 6379,执行成功后主从关系就设置完毕了,之后的执行过程如下:

1:111发送消息[psync ? -1],其中格式[psync runId offset],runId是一个redis实例每次启动时的唯一标识,-1代表偏移量为1,即首次同步
2:110收到psync消息后,回复消息FULLRESYNC {runId} {offset},该消息告知111自己的runId和当前偏移量,到这里可以认为第一阶段完成
3:110执行fork启动子进程,生成rbd快照,并发送给111,111收到快照后,清空本地数据,并加载rbd快照数据到内存,在此期间110会将新产生的修改写到repli buffer中,到这里可以认为第二阶段完成
4:rbd发送完毕后,将repli buffer中的数据也发送给111,到这里首次同步过程完毕

以上的过程可用下图来表述:

redis读写分离解决死锁 redis读写分离实现_redis_03

我们需要特别注意110生成rdb并发送给111的过程,在redis之AOF和RDB持久化 一文分析了频繁的生成RDB会给系统带来的问题,而且在当前的场景下由于还需要将rdb发送给从库,因此又会对主库的网络带宽带来压力,也会直接影响正常的请求处理。在一些redis读压力不断增大的场景下,我们可能会不断的增加从库的个数,其实就会间接的造成主库频繁生成RDB的问题,关于这个问题通过3:主从从模式一起看下。

3:主从从模式

大量的建立从库,会导致主库频繁执行fork生成RDB,此时,我们可以采用主从从,即主从级联的模式来解决这个问题,具体来说就是,新加的从库不从主库同步数据,而是从一个从库来同步数据,即作为该从库的从库,实际操作中我们可以预留一台内存等资源相对充足的机器来充当这个角色,此时这个结构可能如下图:

redis读写分离解决死锁 redis读写分离实现_repl buffer_04

当数据首次的全量同步完成后,就可以持续的通过长连接来将新的写入同步到从库了,这里的同步需要使用到repl_backlog_buffer的内存结构(repl_backlog_size参数设置大小),这是一个环形的结构,类似于MySQL InnoDB存储引擎的redo log ,也是用来循环记录新的写入,通过偏移量来记录位置信息,其中主库记录自己当前写入的位置信息master_repl_offset,从库记录自己当前同步的位置信息slave_repl_offset,可参考下图:

redis读写分离解决死锁 redis读写分离实现_redis读写分离解决死锁_05


当从库因为网络或者其他原因意外断开重连后,数据会进行增量同步,也需要依赖repl_backlog_buffer结构,在从库重连后,会向主库发送psync {runId} {offset},然后主库会将repl_backlog_buffer的offset之后的增量数据发送给从库,之后就再次进入到基于长连接的常规数据同步过程,这个过程可以参考下图:

redis读写分离解决死锁 redis读写分离实现_RDB_06

注意以上分析重连不考虑主库写入repl_backlog_buffer已经将从库还未同步的数据盖掉的情况,如果是主库判断盖掉的话就会执行全量同步过程了。