14 复制
在redis中使用slaveof命令,可以使得服务器去复制另一个服务器,成为那个服务器的从服务器,称为主从复制
14.1 旧版复制功能
旧版复制的实现由两种方式组成
- 同步:通过发送主服务器的rdb文件给从服务器与缓冲区保存后续写命令使得主从服务器状态一致
- 命令传播:主从服务器状态一致后,主服务器发送后续修改数据库的命令给从服务器保证主从服务器状态一致
14.1.1 同步
当一个服务器使用slaveof命令成为另一个服务器的从服务器时,使用SYNC命令启动同步操作,过程如下
- 从服务器发送SYNC命令给主服务器
- 主服务器执行BGSAVE命令生成rdb文件,并使用一个缓冲区保存后续写命令
- 从服务器使用主服务器发送过来的rdb文件同步到和主服务器状态一致
- 主服务将缓冲区中的写命令发送给从服务器执行,使得其状态完全一致
14.1.2 命令传播
为了保证主从服务器状态是一致的,使用同步命令初始化后,后续的持续性的一致性需要命令传播来保证。
命令传播是指在主服务器执行写命令时也同时会把这个命令发送给从服务器,让从服务器再执行一遍。
14.2 旧版复制功能的缺陷
在主从服务器正常连接后,若突然出现主从服务器的断开(非正常断开),在重新连接以后,在断开时间段中由于命令传播功能无法正常执行,导致主从服务器的数据库状态存在差异,此时需要重写进行同步操作。
此时的同步操作会同步所有数据,但其实主从服务器之间只差了断开期间的几条指令。之前的大部分数据完全一样,而再次执行同步操作就显得性价比极低。,并且SYNC命令是一个非常耗费资源的操作{包括一次rdb文件生成(消耗cpu,内存,磁盘I/O资源),rdb文件的发送(带宽与流量),从服务器载入RDB文件(从服务器阻塞)}
14.3 新版复制的实现
新版复制(redis2.8版本以后)使用PSYNC命令代替SYNC命令来执行复制时的同步操作,PSYNC命令具有完整全同步和部分重同步两种模式
- 完整全同步和SYNC命令的同步一样,会使用rdb文件全部复制主服务器的所有数据
- 部分重同步处理旧版复制的缺陷,利用复制偏移量,复制积压缓冲区,服务器运行ID实现只复制需要部分的功能,而不会因为缺少部分数据,而复制所有数据
14.4 部分重同步的实现
部分重同步利用复制偏移量,复制积压缓冲区,服务器运行ID实现只复制需要部分的功能
14.4.1 复制偏移量
主服务器和从服务器都维护了一个复制偏移量,主服务器执行写操作,写N个字节数据,就让主服务器的复制偏移量增加N,每次主服务器向从服务器执行命令传播时,传播N个字节数据,就让从服务器的复制偏移量增加N。
若主从服务器状态一致,那他们的复制偏移量总是一样。
14.4.2 复制积压缓冲区
复制积压区是一个固定长度的循环队列,内部储存的数据结构,每个都保存着偏移量与该偏移量对应一个字节的写命令内容
偏移量 | … | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | … |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
字节值 | … | ‘*’ | 3 | ‘\r’ | ‘\n’ | ‘$’ | 3 | ‘\r’ | ‘\n’ | ‘S’ | ‘E’ | ‘T’ | … |
当从服务器重新连接以后,会发送自己保存的主服务器运行ID与自己的复制偏移量发送给主服务器,若主服务器运行ID与从服务器发送的断线前的主服务运行ID一致,且从服务器的复制偏移量在复制积压缓冲区中未被冲走(从服务器偏移量+1还在循环队列中有保存),就会根据偏移量差,在复制积压缓冲区中取命令,并执行,然后更新从服务器的复制偏移量,使得主从服务器状态一致。
关于缓存区大小的选择:
缓冲区的大小一般根据,服务器重连平均时间与主服务器平均每秒执行写命令数量来估算,选择的大小一般都需要大于这两者的乘积,来保证大多数从服务器重连以后是执行部分重同步,达到节约资源的目的
14.4.3 服务器运行ID
不管是从服务器还是主服务器都有自己的运行ID(四十个随机的16进制字符),从服务器会保存当前连接的主服务器的运行ID,保存的主服务器运行ID用于从服务器使用PSYNC命令时,若从服务器保存的有之前的主服务器运行ID,会把这个运行ID一起发给主服务器,若主服务器发现这个ID和自己的运行ID一样,就会考虑复制偏移量在缓冲区中是否有保存,来决定执行部分重同步还是完整重同步,若运行ID不一样,默认使用完整重同步。
14.5 PSYNC命令的实现
现在,来完整了解PSYNC的实现流程
- 从服务器请求复制
- 如果一个服务器成为从服务器时,没有保存主服务器运行ID(第一次成为主服务器或者执行过
SLAVEOF no one
命令),那么服务器向主服务器发送PSYNC ? -1
,主动请求完整重同步 - 如果一个服务器成为从服务器时,保存的有主服务器运行ID,那么发送
PSYNC <runid> <offset>
将之前连接的主服务器运行ID与自己的复制偏移量发送给主服务器。
- 如果一个服务器成为从服务器时,没有保存主服务器运行ID(第一次成为主服务器或者执行过
- 主服务器回复
- 若主服务器回复+FULLRESYNC 表示将进行完整重同步,主服务器发送的两个参数是自己的运行ID和复制偏移量,从服务器需要保存新的主服务器运行ID和更新复制偏移量
- 若主服务器回复+CONTINUE,表示将进行部分重同步
- 若回复-ERR,表示出错,主服务器版本低于Redis2.8,不能识别PSYNC命令,接下来会发送SYNC命令,进行完整重同步
14.6 复制的实现
使用SLAVEOF <master-ip> <master-port>
可以让从服务器去复制一个主服务器,参数为主服务器的地址和端口
14.6.1 设置主服务器的地址和端口
从服务器将主服务器的地址和端口保存在自己的结构中(redisServer的masterhost属性与masterport属性用于保存主服务器的地址和端口)
14.6.2 建立套接字连接
设置主服务器的地址和端口后,从服务器会为主服务器创建一个套接字并关联一个文件事件处理器来处理复制工作(接收RDB文件和主服务器发送的更新操作),主服务器在接受从服务器套接字连接后,将这个套接字当做客户端处理,并为其创建相应的客户端状态。
从服务器会向主服务器发送命令请求,也会向服务器发送命令请求,所以此时的从服务器同时具有服务器与客户端两个状态。
14.6.3 发送PING命令
从服务器成为主服务器的客户端后,第一件事是给主服务器发送PING命令
- 作用一是检测套接字的读写状态是否正常,通信是否正常进行
- 作用二检查主服务器能否正常处理命令请求
服务器接收到PING命令后回复有三种情况
- 如果主服务器给从服务器进行回复,但从服务器不能在规定时间内读取命令回复的内容,说明主从服务器的网络连接不稳定,此时会断开并重新创建连接主服务器的套接字
- 返回一个错误,表示主服务器暂时没办法处理从服务器的命令请求,此时会断开并重新创建连接主服务器的套接字
- 返回PONG,表示正常连接,继续执行后续复制工作
14.6.4 身份验证
主服务器给从服务器返回PONG以后,若从服务器设置了masterauth选项,那么就会进行身份验证。
身份验证时,从服务器会给主服务器发送命令AUTH <masterauth>
,命令会带上masterauth选项对应的值。
身份验证阶段会发生以下情况
- 若主服务器没有设置requirepass选项,从服务器没有设置masterauth选项,没有验证,复制正常进行
- 若身份验证发送命令中masterauth选项对应的值与主服务器requirepass选项对应的值相同,验证通过,复制工作继续。
- 若身份验证发送命令中masterauth选项对应的值与主服务器requirepass选项对应的值不相同,主服务器返回一个invalid password错误
- 若主服务器设置了requirepass选项,但从服务器没有设置masterauth选项,返回NOAUTH错误
- 若主服务器没有设置requirepass选项,但从服务器设置了masterauth选项,返回no password is set错误
所有错误都会中止复制工作,并从创建套接字开始重新开始复制工作,知道验证通过,或从服务器放弃复制
14.6.5 发送端口信息
身份验证以后,从服务器使用REPLICONF listening-port ,向主服务器发送从服务器的监听端口号。
主服务器接收到以后,会把从服务器的端口号保存到客户端状态的slave_listening_port属性中,这个属性目前唯一的作用是用于执行INFO replication命令时显示当前服务器的从服务器对应客户端状态中打印从服务器的端口号。
14.6.6同步
前面的五步工作完成后,就开始同步操作,根据情况(前面提及的同步操作)选择执行完整重同步或者部分重同步。
14.6.7 命令传播
长期维持主从服务器的状态一致,就需要命令传播,主服务器将写命令发送给从服务器执行。
14.7 心跳检测
命令传播阶段,从服务器默认已每秒一次的频率,向主服务器发送REPLCONF ACK <replication_offset>
命令,replication_offset是从服务器的复制偏移量。
作用:
- 检测网络连接是否正常
- 辅助实现min-slaves选项
- 检测命令丢失
14.7.1 检测网络连接状态
以固定频率发送命令来检测网络连接是否正常,主服务器若在间隔一秒以上没有收到REPLCONF ACK <replication_offset>
命令,就知道网络连接出问题,另外在使用INFO replication命令时,lag属性中可以查看上一次执行REPLCONF ACK <replication_offset>
命令距离现在的时间。这个值一般为0或1,若为其他值说明有问题。
14.7.2 辅助实现min-slaves选项
Redis的min-slaves-to-write和min-slaves-max-lag两个选项可以防止主服务器在不安全的情况下执行写命令
min-slaves-to-write 3
min-slaves-max-lag 10
那么在从服务器的数量少于3个,或者三个从服务器的延迟(lag)值都大于或等于10秒时,主服务器将拒绝执行写命令,这里的延迟值就是上面提到的INFO replication命令的lag值。
14.7.3 检测命令丢失
我们从命令:REPLCONF ACK <replication_offset>
就可以知道,每发送一次这个命令从服务器都会向主服务器报告一次自己的复制偏移量。那此时尽管主服务器发送给从服务器的SET key value丢失了。则主从服务器的复制偏移量就会不一样,主服务器马上就知道了。并到复制缓冲区去寻找对应丢失的命令。