详谈Redis的主从复制

​ Redis虽然读取写入的速度都特别快,但是也会产生读压力特别大的情况,为了分单读压力,Redis支持主从复制,下面我们就详细了解一下Redis的主从复制是怎么设计的……

1. 概念理解

redis同步有两个命令:sync和psync,前者是redis2.8之间的同步命令,后者是redis2.8为了优化sync新设计的命令

psync需要知道三个概念:

  • 主从节点各自复制偏移量
  • 主节点复制挤压缓冲区
  • 主节点运行ID

主节点复制挤压缓冲区:

  • 复制挤压缓冲区是一个保存在主节点的一个固定长度的先进先出的队列,默认1MB.
  • 这个队列是在slave连接时创建,这时主节点相应写入命令时,不但会把命令发送给从节点,也会写入复制缓冲区
  • 他的作用就是用于部分复制和复制命令丢失的数据补救,通过info replication可以看到相关信息

主节点运行id:

  • 每个redis启动的时候都会生成一个40位的运行ID.

  • 运行ID的主要作用是用来识别Redis的节点,如果使用ip+port,那么如果主节点重启修改RDB/AOF数据,从节点再基于偏移量进行复制将是不安全的。所以当运行id变化后,从节点将进行全量复制,也就是说,redis重启后,默认从节点会进行全量复制

    问题:如果在重启时不改变运行ID呢?

    ​ 1.可以通过debug reload命令重新加载RDB并保持运行id不变,从而有效避免不必要的全量复制

    ​ 2.缺点时,debug reliad命令是一个阻塞命令,会阻塞当前Redis节点主线程,因为对于大数据量的主节点或者无法容忍阻塞的节点,需要谨慎使用,一般通过故障转移机制可以解决这个问题。

psync的执行流程:

命令格式为 psync{runId}{offset}

  runId:从节点所复制主节点的运行 id

  offset:当前从节点已复制的数据偏移量

从节点发送psync命令给主节点,runid就是目标主节点的ID,如果没有默认为-1,offset是从节点保存的复制偏移量,如果第一次复制则为-1

主节点会根据runid和offset决定返回结果:

​ 1.如果回复+FULLRESYNC{runid}{offset},那么从节点将触发全量复制流程。

​ 2.如果回复+CONTINUE,从节点将触发部分复制。

​ 3.如果回复+ERR,说明主节点不支持2.8 psync命令,将使用sync执行全量复制。

复制过程

​ 1.从节点执行slaveof命令

​ 2.从节点保存了slaveof命令中主节点的信息,并没有立即发起复制

​ 3.从节点内部的定时任务发现有主节点的信息,开始使用socket连接主节点

​ 4.连接建立成功后,发送ping命令,希望得到ping命令的响应,否则会进行重连。

​ 5.如果有权限,权限验证成功后则进行数据同步,主节点将把所有的数据全部发送给从节点(同步数据集)

​ 6.当主节点把当前的数据同步给从节点后,则完成了复制的建立流程,接下来就会持续把写命令发送给从节点,保证从数据一致性。

全量复制

从节点第一次连接主节点肯定触发的是全量复制,流程如下:

​ 1.slave发送psync命令(psync ? -1)

​ 2.主节点根据命令返回FULLRESYNC

​ 3.从节点记录主节点的ID和offset

​ 4.主节点bgsave并保存RDB到本地

​ 5.主节点发送RDB文件到从节点

​ 6.从节点收到RDB文件并加载到到内存中

​ 7.主节点在从节点接收数据的期间,将新请求的数据保存到“复制客户端缓冲区”,当从节点加载RDB完毕后,再发送过去(如果从节点花费时间过长,将导致缓冲区溢出,最后全量同步失败)

​ 8.从节点清空自己旧数据然后加载RDB文件,如果RDB文件过大,这一步仍然耗时,如果此时客户端访问,将导致数据不一致,可以使用配置slave-server-stale-data关闭。

主从复制期间对于主从服务器都是非阻塞的,但是从服务器来说在主从复制期间取到的是旧数据,也可以配

置文件中配置成阻塞的,当有客户端请求时候返回错误信息,配置如下:

slave-serve-stale-data参数设置成yes,主从复制中,从服务器可以响应客户端请求;

slave-serve-stale-data参数设置成no,主从复制中,从服务器将阻塞所有请求,有客户端请求时返回“SYNC

with master in progress”;

​ 9.从节点成功加载完RDB后,如果开启AOF,会立刻做bgrewriteaof

1.如果RDB文件大于6GB,并且是千兆网卡,Redis的默认超时机制(60s),会导致全量复制失败。可以通过调大repl-timeout参数来解决此问题

2.Redis虽然支持无盘复制,也就是说主节点直接通过网络发送给从节点,这样可以提高同步效率,但是需要稳定高带宽和磁盘读写速度保证,生产上还是建议慎用

部分复制

场景:当从节点正在复制主节点,如果出现网络闪断和其他异常,从节点会让主节点补发丢失的命令数据,主节点只需要将复制缓冲区的数据发送到从节点就能够保证数据一致性,相比较全量复制,成本开销比较小。

​ 流程如下:

​ 1.当从节点出现网络中断,超过了repl-timeout时间,主节点就会中断复制链接。

​ 2.主节点会将请求的数据写入“复制挤压缓冲区”,默认1MB。

​ 3.当从节点恢复重新连接上主节点,请求继续复制,从节点会将offset和主节点的id发送给主节点

假如主从服务器的两个master run id相同,并且指定的偏移量在内存缓冲区中还有效,复制就会从上次中断的 点开始继续。如果其中一个条件不满足,就会进行完全重新同步

​ 4.主节点校验后,如果偏移量数后的数据在缓冲区,就发送CONTINUE相应,进行部分复制

​ 5.主节点将缓冲区的数据发送到从节点,保证了主从复制进行正常状态