【Redis】主从复制

一、旧版复制功能的实现

旧版本Redis(2.8版本)的复制功能可以分为全量同步和命令传播两个操作:

  • 同步操作用于将从服务器的数据库状态更新至主服务器当前所处的状态。
  • 命令传播操作则用于在主服务器的数据库状态被修改,导致主从服务器的数据库状态不一致时,让主从服务器的数据库重新回到一致状态。

1.1、全量同步

1.1.1、步骤

当通过客户端向从服务器发送 SLAVEOF 命令,要求从服务器复制主服务器时,从服务器就要执行同步操作,即将从服务器的数据库状态更新至主服务器当前所处的数据库状态。同步过程步骤如下:

  • 从服务器向主服务器发送 SYNC 命令。
  • 收到SYNC命令的主服务器执行 BGSAVE 命令,在后台生成一个RDB文件,并使用一个缓冲区记录从现在开始执行的所有写命令。
  • 当主服务器的BGSAVE命令执行完毕,主服务器将生成的RDB文件发送给从服务器,从服务器接收并加载这个RDB文件,将自己的数据库状态更新至主服务器执行BGSAVE命令时的数据库状态。
  • 主服务器将记录在缓冲区里面的所有写命令发送给从服务器,从服务器执行这些写命令,将自己的数据库状态更新至主服务器当前所处的状态。

1.1.2、图解

【Redis】主从复制_第1张图片

1.2、命令传播

在同步操作完毕后,主从服务器两者的数据库状态达到一致,但是如果客户端发送写命令给主服务器时,将导致主从服务器状态不一致。

为了让主从服务器数据一致,主服务器需要对从服务器执行命令传播操作:主服务器会将自己执行的那条写命令,发送给从服务器执行,当从服务器执行了该写命令后,主从服务器将再次回到一致状态。

在Redis中,从服务器对主服务器的复制可以分为以下两种情况:

  • 初次复制:从服务器以前没有复制过任何主服务器或者上次复制的主服务器和这一次不是同一个主服务器。
  • 断线后重新复制:处于命令传播阶段的主服务器因为网络原因而中断复制,但从服务器通过自动重现连接上了主服务器,并继续复制主服务器。
    【Redis】主从复制_第2张图片

1.3、严重缺陷

主服务器在时间T0到T10086过程中一直处于一致状态。这两个服务器的数据是一样的。从服务器断开重连后,只需要主服务器的K10087,K10088,K10089三个命令即可,但是主服务器却返回了一个新的RDB文件,如果RDB文件很大,必将导致严重的内存问题。

二、新版复制功能的实现

为了解决旧版的问题,Redis2.8 以后引入了PSYNC代替SYNC命令执行复制时的同步操作。

  • PSYNC命令具有完全同步和部分重同步功能。
  • 完全同步和旧版的全量同步是一样的。
  • 部分同步主要是用于断线后的重复制情况:当从服务器断开重新连接后,如果条件允许,主服务器将断开期间的写命令发给从服务器,从服务器执行断开期间的写命令完成同步,将数据库更新到和主服务器一致。避免使用全量的 RDB 文件同步

2.1、全量同步

完全同步和旧版的全量同步是一样的。

2.1.1、步骤

  • slave 请求master BgSave出自己的一个RDB Snapshot文件发给slave
  • slave接收完毕后,清除掉自己的旧数据
  • 将RDB载入内存

2.1.2、图解

【Redis】主从复制_第3张图片
上图简要说明了Redis中Master节点到Slave节点的全量数据同步过程。当Slave节点给定的run_id和Master的run_id不一致时,或者Slave给定的上一次增量同步的offset的位置在Master的环形内存中无法定位时(后文会提到),Master就会对Slave发起全量同步操作。这时无论您是否在Master打开了RDB快照功能,它和Slave节点的每一次全量同步操作过程都会更新/创建Master上的RDB文件。

2.2、增量同步

2.2.1、步骤

图中说明

2.2.2、图解

在Slave连接到Master,并完成第一次全量数据同步后,接下来Master到Slave的数据同步过程一般就是增量同步形式了(也称为部分同步)。增量同步过程不再主要依赖RDB文件,Master会将新产生的数据变化操作存放在一个内存区域,这个内存区域采用环形构造。
【Redis】主从复制_第4张图片

2.2.3、案例

【Redis】主从复制_第5张图片

主服务器和从服务器都会维护一个复制偏移量,主要是用户对比复制的执行结果。例如主从服务器的复制偏移量均为1000,当主服务器完成了3个写命令后,主服务器偏移量为1003,这时候将3个命令给从服务器执行,从服务器执行完毕,复制偏移量也为1003。
如果主从服务器的数据是一致的,那么他们的偏移量也是一致的。

复制积压缓冲区,在主服务器将写命令给从服务器后,还会写入到复制积压缓冲区,如果执行到了偏移量为1003的时候,从服务器A断开,主服务器继续执行了7个写入命令,这时候主服务器的偏移量为1100,A服务器连接上,请求复制主服务器,这时候主服务器会分情况处理。- 主服务器不是A从服务器之前复制的主服务器(根据服务器ID判断),执行完全同步。

  • 发现A从服务器之前复制的是自己。根据A从服务器的偏移量去复制积压缓冲区中看1004-1100命令是否依然存在,如果存在将1004-1100偏移量的命令返回给A从服务器执行。如果不存在,只能执行完全同步恢复数据一致了。

三、主从复制配置

slave可以在配置文件、启动命令行参数、以及redis-cli执行SlaveOf指令来设置自己是slave。
测试表明同步延时非常小,指令一旦执行完毕就会立刻写AOF文件和向Slave转发,除非Slave自己被阻塞住了。
比较蠢的是,即使在配置文件里设了slavof,slave启动时依然会先从数据文件载入一堆没用的数据,再去执行slaveof。
“Slaveof no one”,立马变身master。
2.8 版本将支持PSYNC部分同步,master会拨出一小段内存来存放要发给slave的指令,如果slave短暂的断开了,重连时会从内存中读取需要补读 的指令,这样就不需要断开两秒也搞一次全同步了。但如果断开时间较长,已经超过了内存中保存的数据,就还是要全同步。
Slave也可以接收Read-Only的请求。

Redis提供的主从复制功能的配置信息,在Redis主配置文件的“REPLICATION”部分。以下是这个部分的主要参数项说明:

slaveof <masterip> <masterport>:如果您需要将某个节点设置为某个Master节点的Slave节点,您需要在这里指定Master节点的IP信息和端口信息。这个设置项默认是关闭的,也即是说Master节点不需要设置这个参数。另外,除了通过配置文件设置外,您还可以通过Redis的客户端命令进行slaveof设定。

slave-serve-stale-data:当master节点断开和当前salve节点的连接或者当前slave节点正在进行和master节点的数据同步时,如果收到了客户端的数据读取请求,slave服务器是否使用陈旧数据向客户端提供服务。该参数的默认值为yes。

slave-read-only 是否将salve节点设置为“只读”。一旦设置为“只读”,表示这个Salve节点只会进行数据读取服务,如果客户端直接向这个Salve节点发送写数据的请求,则会收到错误提示。建议采用默认的“yes”值进行设定。

repl-diskless-sync:上文已经介绍过Redis的主从复制功能基于RDB,后者的过程是将数据刷入RDB文件(实际上是Linux的Page Cache区域),然后基于RDB文件内容的更新情况和Salve当前已同步的数据标记点来进行Salve上的数据更新。所以这个过程实际会增加一定的数据延迟,消耗一定的处理资源。基于这个情况,Redis中提供了一种不经过物理磁盘设备就进行主从数据同步的技术,称为diskless。但是直到Redis version 3.2这个技术也一直处于试验状态,所以并不推荐在生产环境下使用:“ 
WARNING: DISKLESS REPLICATION IS EXPERIMENTAL CURRENTLY”。

repl-diskless-sync-delay:这个参数只有在上一个参数设置为“yes”时才起作用,主要是设置在进行两次diskless模式的数据同步操作的时间间隔。默认为5秒。

repl-ping-slave-period:Slave节点向Master节点发送ping指令的事件间隔,默认为10秒。

repl-timeout:这是一个超时间,当某些操作达到这个时间时,Master和Slave双方都会认为对方已经断开连接。实际上您可以将这个时间看成是一个租约到期的时间。那么这个操作时间会影响哪些操作呢?A、向Slave进行的数据同步操作本身不能超过这个时间;B、Slave向Master发送一个PING指令并等待响应的时间;C、Master向Slave发送PONG回复并等待ACK的时间。

repl-disable-tcp-nodelay:这个选项的默认值为no,它对优化主从复制时使用的网络资源非常有用。要明白这个参数的含义,就首先要解释一下tcp-nodelay是个什么玩意儿?TCP数据报的报文头包含很多属性,这些属性基本上起到记录和保证传输目的、传输状态的作用,但没有数据报的所携带的业务数据(称之为有效载荷)。那么很明显,20个字节内容的信息分成20个数据报进行传输和只用一个数据报进行传输,需要占用的网络资源就完全不一样。JohnNagle在1984年发明了一种减轻网络传输压力的算法,就是为了解决这个问题(算法的名字就叫做“Nagle”,后续的技术人员又做了很多改进和升级)。其基本思路就是将要发送的内容凑够一定的数量后,再用一个数据报发送出去。如果该属性设置为yes,Redis将使用“Nagle”算法(或类似算法),让数据报中的有效载荷凑够一定数量后,在发送出去;设置成no,Redis就不会这么做。

repl-backlog-size:上文已经介绍过了Redis中为了进行增量同步所准备的环形内存区域,以及Redis这样做的原因额,所以这里就不再赘述了。这个选项就是用来设置环形内存的大小的,这个选项的默认值为1MB;正式的生产环境下可以稍微加大一些,例如5MB。

slave-priority:当前Slave节点的优先级权重。我们后文会介绍一款Redis自带的监控和故障转移工具:Redis Sentinel,这个工具允许一个Master节点下有多个Slave节点,并且可以自动切换Slave节点为Master节点。如果Slave节点的优先级权重值越低,就会再切换时有限成为新的Master节点。

min-slaves-to-write和min-slaves-max-lag:为了尽可能避免Master节点对应的多个Slave节点在数据复制过程中数据差异被越拉越大。Redis服务提供了一组拒绝数据写操作的策略,这个策略可以解释为:当Master上在min-slaves-max-lag时间(单位秒)间隔后,任然有min-slaves-to-write个Slave和它正常连接,那么Master才允许进行数据写操作。

你可能感兴趣的:(【Reids】)