Redis14:复制
复制
假设现在有两个redis服务器,地址分别为127.0.0.1:6379和127.0.0.1:12345,如果我们向服务器127.0.0.1:12345发送以下命令:
slaveof 127.0.0.1 6379
那么前者就是后者的主服务器,后者是前者的从服务器。进行复制中的主从服务器双方的数据库将保存相同的数据,这就是数据库状态一致。如果我们在主服务器中设置某个键的值或者删除某个键,从服务器也会进行相同的操作。
旧版本(2.8前)复制功能的实现
redis的复制功能分为同步和命令传播两个操作:
1、同步是将从服务器的数据库状态更新至主服务器当前所处的数据库状态
2、命令传播用于当主服务器的数据库状态被修改,导致主从服务器的数据库状态出现不一致时,让主从服务器的数据库重新回到一致状态。
同步
当服务器收到slaveof命令后,从服务器首先需要执行的就是同步操作,步骤如下:
1、从服务器向主服务器发送sync命令。
2、主服务器收到命令后,开始执行bgsave命令,在后台生成一个RDB文件,并使用一个缓冲区记录从现在开始执行的所有写命令。
3、主服务器bgsave命令执行完毕后,主服务器会将RDB文件发给从服务器,从服务器接收并载入这个文件,载入完成后主服务器将记录在缓冲区内的所有写命令发给从服务器,从服务器执行这些命令,将自己的数据库状态更新到主服务器当前的状态。
命令传播
当主从服务器两者达到一致状态后,主服务器执行写命令后,主从服务器状态不再一致,此时主服务器需要对从服务器执行命令传播操作,也就是主服务器会将自己执行的写命令发送给从服务器执行,当从服务器执行后主从服务器再次回到一致状态。
旧版新版复制功能对比
旧版复制功能的缺陷
在redis中从服务器对主服务器的复制可以分为以下两种情况:初次复制和断线后重新复制,对于前者来说旧版复制功能可以很好的完成,但是对于后者情况,旧版复制功能效率较低。
当主从服务器通过同步达到一致状态后,主服务器就通过命令传播来保持一致性,当断线发生后命令传播无法继续,重新连接后旧版复制功能会向主服务器发送sync命令,该命令会让主服务器把从同步完成开始到断线重连的所有写命令都转化成RDB文件发送给从服务器,但是其实根本不需要那么多,只需要传送断线发生后到断线重连这段时间的写命令即可。断线期间执行的写命令越少,效率浪费就越严重。
新版复制功能的实现
redis从2.8版本开始就用psync命令来代替sync命令,psync命令具有完整重同步和部分重同步两种模式:
1、完整重同步模式和sync命令的效果基本相同:主服务器创建并发送RDB文件,然后发送缓冲区记录的写命令。
2、部分重同步模式用于断线重连情况,主服务器可以将断开后到重连完期间的命令发送给从服务器,实现效率的提升(但是特殊情况下还是要发送RDB文件)
部分重同步的实现
部分重同步功能的实现依赖三个部分:主从服务器的复制偏移量、主服务器的复制积压缓冲区、服务器的运行ID。
复制偏移量
主服务器要实现断线重连后能精准的将断线到重连部分的写命令发送给从服务器,就必须通过一种机制来记录命令发送进度,主从服务器都会维护一个复制偏移量:主服务器每次向从服务器发送N个字节的数据时就将自己的复制偏移量加N,从服务器每次收到主服务器传播来的N个字节的数据时,也会将自己的复制偏移量加N。
如果主从服务器的偏移量不同说明两者处于不一致状态,当主服务器的偏移量比某个从服务器大的时候,就说明这个从服务器断线了,断线重连后从服务器向主服务器发送psync命令,将自己的复制偏移量报告给主服务器,主服务器经过对比就可以知道从服务器没有的是哪段写命令了。
复制积压缓冲区
复制积压缓冲区是主服务器维护的一个环形队列,当主服务器进行命令传播时会将发送给从服务器的写命令存入这个缓冲区内,缓冲区的大小是有限的,只会保存一部分最近传播的写命令。
当从服务器断线重连主服务器后,主服务器会根据从服务器发送过来的复制偏移量offset和缓冲区的情况决定接下来的操作:
1、如果offset之后的数据还存在复制积压缓冲区中,主服务器会进行部分重同步操作
2、如果offset之后的数据已经被覆盖掉,主服务器就会进行完整重同步操作
复制积压缓冲区默认大小为1MB,如果主服务器执行大量写命令,而断线时间又较长,就会导致无法进行部分重同步,效率浪费严重,所以复制积压缓冲区的大小要设置合理,可以根据断线时间估计值乘以每秒产生的写命令数据量来设置,缓冲区的大小可以通过配置文件的repl-backlog-size选项来设置。
服务器运行ID
每个redis服务器都有自己的ID,这个ID在服务器启动时自动生成。当从服务器对主服务器进行初次复制时,主服务器会将自己的ID传送给从服务器,从服务器会将这个ID保存起来。
当从服务器断线重连上一个主服务器时,从服务器向主服务器发送之前保存的ID,如果这个ID和主服务器ID是一个,那么主服务器就尝试进行部分重同步操作,如果不是一个,说明之前的主服务器和现在的不是一个,主服务器将进行完整重同步操作。
psync命令的实现
psync命令调用细节:
从服务器根据不同情况发送psync命令的不同形式:
1、从服务器在开始一次新的复制时将向主服务器推送下列命令:
psync ? -1
代表主动请求主服务器进行完整重同步。
2、如果从服务器已经复制过某个主服务器,那么在开始一次新的复制时将向主服务器发送:
psync <ID> <offset>
ID就是上一次复制的服务器的运行ID,offset是从服务器当前的复制偏移量。
收到psync命令的主服务器会根据不同情况返回以下几种回复:
1、主服务器将与从服务器执行完整重同步操作:
+fullresync <ID> <offset>
同时将自己的运行ID和offset发送给从服务器,从服务器会将ID保存起来,将自己的偏移量初始化为offset。
2、主服务器将执行部分重同步操作:
+continue
主服务器将发送从服务器缺少的那部分写命令。
3、主服务器不能识别psync命令:
-err
这主要是因为主服务器的版本低于redis2.8,它识别不了psync命令,从服务器此时会向主服务器发送sync命令。
![](https://img2020.cnblogs.com/blog/1742600/202006/1742600-20200627234327842-759306670.png)
slaveof的实现
当执行slaveof时可以让一个从服务器去复制一个主服务器:
slaveof 主服务器ip 端口
设置主服务器ip和端口
当从服务器收到slaveof命令时,从服务器就会设置服务器状态中的masterhost和masterport属性,分别代表主服务器的地址和端口。设置完成后从服务器会向发送slaveof命令的客户端返回OK,然后执行下列步骤。
建立套接字连接
从服务器根据ip地址和端口创建连向主服务器的套接字地址,如果套接字能成功连接到主服务器,从服务器会为这个套接字关联一个用于处理文件复制工作的文件事件处理器,这个处理器负责后续的接受RDB文件、接受写命令等。
主服务器接收套接字连接后,会为该套接字创建相应的客户端状态,主服务器会把从服务器看做一个连接到主服务器的客户端,此时他们的关系:
![](https://img2020.cnblogs.com/blog/1742600/202006/1742600-20200627234343391-495255287.png)
发送PING命令
从服务器成为主服务器的客户端之后,就会向主服务器发送一个ping命令,此时:
1、如果主服务器向从服务器返回了一个命令回复,但从服务器不能在规定的时限中读取回复内容,就证明网络连接状态不佳,此时从服务器断开并重新创建连向主服务器的套接字。
2、如果主服务器向从服务器返回一个错误,代表主服务器目前暂时没办法处理从服务器的命令请求,不能执行赋值操作。
3、主服务器回复PONG,代表网络连接正常,从服务器可以继续执行下列步骤。
身份验证
如果此时从服务器设置了masterauth选项,那么就要进行身份验证。如果从服务器配置的masterauth选项的值是10086,那么此时从服务器就会向主服务器发送命令:
auth 10086
此时:
1、主服务器的requirepass选项和10086相同,那么验证通过,如果不同则主服务器返回一个错误
2、如果主服务器没有设置requirepass选项,将返回一个no password is set错误
所有错误都会导致从服务器终止目前的复制工作,从创建套接字开始重新执行流程,直到身份通过或者从服务器放弃复制。
发送端口信息给主服务器
身份验证之后,从服务器会向主服务器发送:
replconf listening-port portnumber
portnumber就是从服务器的监听端口号。主服务器收到该命令后,会将该端口号存入对应客户端状态的slave_listening_port属性中,在主服务器执行:
info replication
时,就会打印其从服务器的端口号。
同步和命令传播
从服务器向主服务器发送psync命令,执行该命令后,主服务器也会成为从服务器的客户端,因为发送缓冲区内的写命令需要从服务器来接受。此时他们的关系:
![](https://img2020.cnblogs.com/blog/1742600/202006/1742600-20200627234400639-1619241224.png)
完成同步后,主服务器会进行命令传播,把写命令持续的发送给从服务器。
心跳检测
在命令传播阶段,从服务器默认会以1秒一次的频率向主服务器发送:
replconf ack <offset>
其中offset是从服务器的复制偏移量。
检测网络连接状态
主服务器如果超过1秒没有收到来自从服务器的replconf ack命令,那么网络连接状态就出现问题了。
可以向主服务器发送以下命令查看连接状态:
info replication
打印结果中的slave:lag就代表距离上一次收到ack命令的秒数,正常应该在0和1之间跳动。
min-slaves选项
如果我们向主服务器提供下列配置:
min-slaves-to-write 3
min-slaves-max-log 10
那么在从服务器数量少于3个,或者三个从服务器的延迟值lag都大于等于10秒时,主服务器将拒绝执行写命令。这个配置可以让主服务器在不安全的情况下不执行写命令。
检测命令丢失
因为网络故障,主服务器传播给从服务器的写命令可能会在半路丢失,在旧版本这种丢失是无法避免的,但是在新版本主服务器在心跳检测时可以对比两个复制偏移量,如果主服务器发现从服务器的复制偏移量比自己的要小,就从复制积压缓冲区中将最近的写命令取出,将从服务器缺少的写命令发送给从服务器。