Redis主从复制总结

主从复制简介

Redis的主从复制功能主要用来支持多个Redis节点主从之间的数据同步。默认情况下启动的Redis节点都是主节点,我们可以通过slaveof 或者replicaof命令可以将一个Redis节点设置为另外一个节点的备节点。默认情况下此时主节点可以进行读写操作,当发生写操作的时候自动将数据同步到从节点,而从节点一般是只读的,并接收主节点同步过来的数据,一个主节点可以有多个从节点,而一个从节点只能有一个主节点。Redis的主从功能可以带来哪些好处呢。

  • 可靠性:一般系统为了解决单点故障,都会把数据复制成多个副本部署到其它节点,Redis为我们提供的复制功能,实现了数据的多副本复制,结合Redis的哨兵机制或者集群机制,便可以实现Redis的高可靠性。
  • 高并发: 通过redis的复制功能可以很好的实现数据的读写分离,例如主节点主要进行写操作,而从节点负责读操作。读写分离一般来说是用来支撑高并发,写的请求比较少,大量的请求都是读,如果对数据的一致性要求不是那么强,那么Redis的主从架构就是比较好的选择, 主从架构依赖的基础之一便是Redis的主从复制功能。

基本过程:

整个复制过程的最核心的代码可以参看replication.c源文件,我觉得Redis代码最重要的一个特征是状态转移(状态机)编程,其中复制功能过程就涉及到一系列动作及其server.repl_state相关状态的变化。我们根据其状态变化可以了解到其复制代码基本流程,详见下图。

  • 1 ,从节点执行 slaveof 命令,从节点会做一些准备动作,并保存了 slaveof 命令中主节点的信息,然后将 server.repl_state 设置为REPL_STATE_CONNECT,此时 并没有立即发起复制。
  • 2,从节点内部的定时任务发现有主节点的信息,开始使用 socket 连接主节点,连接建立成功后,发送 ping 命令,希望得到 pong 命令响应,否则会进行重连。如果主节点设置了权限,那么就需要进行权限验证;如果验证失败,复制终止。权限验证通过后,还能进行IP,port相关等一些处理,在4.0版本以后可能还会判断支不支持psync2同步协议。然后server.repl_state会进入REPL_STATE_SEND_PSYNC状态。
  • 3 随后会进入到最重要耗时的操作:进行数据同步。备节点会尝试看看能不能进行半同步操作,第一次建立连接时必定进行一次全同步,其它情况下主节点会根据从节点发过来的psync_offset进行判断是否需要进行全同步。如果进行全同步,主节点将把所有的数据全部发送给从节点。从节点装载完数据之后,接下来,主节点就会持续的把写命令发送给从节点,尽可能保证主从数据一致性。最后server.repl_state会进入到REPL_STATE_CONNECTED状态,代表主从关系已经正常建立。


    Redis主从复制总结_第1张图片
    Redis server.repl_state状态转换图

复制缓冲区

在这里我们将详细地介绍下上面基本过程提到的第3步,备节点尝试是否能进行半同步,主要通过发送以下命令给主节点: psync {runId} {offset}

runId : 从节点所复制主节点的运行 id
offset:当前从节点已复制的数据偏移量

是否能进行半同步,除了对runId的比较,以判断主节点是不是之前相关的主节点,其中最重要且最复杂的一个比较便是offset的比较,这个涉及到配置文件中一个很重要的参数repl-backlog-size。

Redis主节点启动之后维护了一个固定长度复制积压缓冲区,其大小便是由repl-backlog-size决定的,默认大小为1MB,其数据结构我们可以看做一个循环队列,循环队列写满之后,最新的数据会覆盖掉旧的历史数据。当主服务器进行命令传播时,它不仅会将写命令发送给所有连接的从服务器,还会将写命令入队到复制积压缓冲区里面。我们可以抽象地将这个缓冲区类比成一个固定容量的池子,池子存储满了之后,里面缓存的永远是固定大小的Redis主节点写入的最新命令,超过此大小的命令将会被排除(覆盖)掉。下面我们回到Redis源代码, 下面是此数据结构相关参数:

    char *repl_backlog;             /* Replication backlog for partial syncs */
    long long repl_backlog_size;    /* Backlog circular buffer size */
    long long repl_backlog_histlen; /* Backlog actual data length */
    long long repl_backlog_idx;     /* Backlog circular buffer current offset,
                                       that is the next byte will'll write to.*/
    long long repl_backlog_off;     /* Replication "master offset" of first
                                       byte in the replication backlog buffer.*/

repl_backlog:代表循环缓冲区
repl_backlog_size: 缓冲区的大小
repl_backlog_histlen: 缓冲区实际存储的命令大小,最多存满整个缓冲区,这些命令是主需要同步到备节点的命令,而备节点可能还没有接收到这些命令。
repl_backlog_idx: 新的命令需要放入此缓冲区时,从这个位置开始存放
repl_backlog_off: 跟master_repl_offset相关,其中master_repl_offset代表从Redis节点启动开始,总共记录的需要复制的命令数目,是一直单调向上增长的。而server.repl_backlog_off = server.master_repl_offset - server.repl_backlog_histlen + 1; 也就是在主偏移为master_repl_offset的时候,缓冲区缓存了数目为repl_backlog_histlen命令之后,这个缓冲区缓存的所有命令第一个命令偏移起始的索引。下图则是复制缓冲区大小为8的简单示意图,上半部分是master_repl_offset 是3,存储4个命令,下半部分是缓冲区已满存储了8个命令时,master_repl_offset 是12,此时各个索引大小。

Redis主从复制总结_第2张图片
复制缓冲区示意图

所以当备节点发送过来的半同步命令偏移psync_offset是如下情况时:

        psync_offset < server.repl_backlog_off ||
        psync_offset > (server.repl_backlog_off + server.repl_backlog_histlen)

例如缓冲区存储情况如上图下半部分所示时,psync_offset =3或者14时,如果那么代表此时备节点需要补充的命令已经有一部分没有在缓冲区中了,或者备节点的数据有点太超前了,无法保持一致性,此时则需要全同步。否则只需要进行半同步即可,此时主节点将连接的客户端转加上CLIENT_SLAVE标记,设置为备客户端,加到备客户端队列。同时将psync_offset后面到最新的master_repl_offset通过此备客户端同步到备节点。

全同步和客户端输出缓冲区

全同步对于Redis来说是一个比较消耗资源的操作,整个过程步骤比较繁多,会对主机也会造成较大的资源消耗,因此在一般情况下,我们是应该尽量避免Redis全同步的。下面我们将详细地分析下Redis全同步的过程,以及此过程中另一个相关联的比较重要的参数client-output-buffer-limit,与备客户端缓冲区相关的参数。

如上节所说,如果主节点认为要进行全同步,将会向备节点发送+FULLRESYNC {runId} {offset} ,备节点将触发全量复制流程。全同步是一个比较复杂且耗时的过程,基本流程如下:


Redis主从复制总结_第3张图片
全同步流程
  • 1,主节点根据命令返回 FULLRESYNC
  • 2,从节点记录主节点 ID 和 offset
  • 3,主节点启动子进程进行bgsave过程,保存 RDB 文件到本地,此时主节点主进程继续响应其它命令的化,则备客户端输出缓冲区开始缓存命令,主节点完成bgsave后发送 RBD 文件到从节点
  • 4,从节点收到 RDB 文件,会清除旧数据,然后将RDB文件并加载到内存中
  • 5,从节点再接收备客户端输出缓冲区的数据
    整个全同步时间消耗包括:主节点bgsave的时间,RDB文件传输时间,从节点清空数据时间,从节点装载RDB文件时间,如果从节点开启了AOF,从节点还需马上进行AOF重写。
    而备客户端输出缓冲区在主节点启动bgsave子进程的过程时就开始缓冲数据,一直到从节点完成RDB文件加载,默认配置情况下client-output-buffer-limit slave 256MB 64MB 60,如果60秒内缓冲区变化超过64MB,或者直接超过了256MB时,主节点将主动关闭备客户端缓冲,直接造成全同步失败,然后会再次请求发起全同步。因此我们要根据主节点的业务情况,RDB文件的大小,网络情况,估算出client-output-buffer-limit合理的配置,尽量避免这种情况导致的全同步失败。配置不合理,严重的话,会导致全同步不断失败,不断循环,日志观察到一直在不断发生全同步,主机一直处于资源高消耗的状态。

[2082] 27 May 19:52:08.449 * Slave 10.255.4.41:6379 asks for synchronization
[2082] 27 May 19:52:08.449 * Unable to partial resync with slave 10.255.4.41:6379 for lack of backlog (Slave request was: 2464643545740).
[2082] 27 May 19:52:08.449 * Waiting for end of BGSAVE for SYNC
[2082] 27 May 19:52:34.797 # Client id=19424116 addr=10.255.4.41:55692 fd=39 name= age=26 idle=26 flags=S db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=15932 oll=2089 omem=302915144 events=r cmd=psync scheduled to be closed ASAP for overcoming of output buffer limits.
[2082] 27 May 19:52:34.836 # Connection with slave 10.255.4.41:6379 lost.

心跳

主从节点在建立复制后,他们之间维护着长连接并彼此发送心跳命令。主从节点都有心跳检测机制,各自模拟成对方的客户端进行通信,通过 client list 命令查看复制相关客户端信息,主节点的连接状态为 flags = M,从节点的连接状态是 flags = S。

  • 1, 主节点默认每隔 10 秒对从节点发送 ping 命令,可修改配置 repl-ping-slave-period 控制发送频率。
  • 2, 备节点在主线程每隔一秒发送 replconf ack{offset} 命令,给主节点上报自身当前的复制偏移量,同时会更新repl_ack_time时间。
  • 3,主节点每隔1秒钟会去检查备节点超时时间,默认如果超过60 秒,则判断备节点下线,会释放其连接。这个参数可由repl-timeout配置。

总结

本文主要首先介绍Redis主从复制的好处,然后介绍了其基本过程,以及与其相关的很重要的两个配置参数复制缓冲区和备客户端输出缓冲区,以及由其可能触发的全同步问题。最后简单介绍了Redis主备之间的心跳机制。

你可能感兴趣的:(Redis主从复制总结)