redis的复制,主要是两步:
1. 同步:全量复制RDB文件。从服务器发送SYNC
命令,主服务器生成RDB文件,传输给从服务器
2. 命令传播:增量同步新的写命令。主服务器将新的写命令发送给从服务器,从而一直保持二者的数据同步
复制的问题
复制如果一直正常最好,如果出现掉线的问题,那么重新连接之后,如何再次同步呢?
在旧版(2.8以前),每次掉线重连,主服务器都会再次全量同步RDB文件,这会造成很大的资源浪费。并且命令传播同步数据的方式也是没有确认的,命令传播出去就不管了。
在新版中,服务器使用PSYNC
命令。增加了类似于TCP的ack机制的东西,叫做复制偏移量。复制偏移量结合复制积压缓冲区,配合服务器的运行ID,可以解决旧版掉线重连时复制低效的问题。
* 复制偏移量:主服务器和从服务器都有这个字段,用于记录当前复制的命令的进度,当掉线重连的时候,从服务器发送PSYNC
命令,同时携带自身的复制偏移量,主服务器通过判断二者的偏移量,可以快速将缺少的命令发送给从服务器,无需发送整个RDB文件,这称为部分重同步。
* 复制积压缓冲区:为了能够快速将缺少的命令发送给从服务器,还需要一个缓冲同步历史命令的缓冲区,就是复制积压缓冲区。这是一个固定大小的先进先出的队列。也就是说,如果需要放入的元素多于队列长度,队列会将先放入的元素弹出,供后面元素入队。主服务器判断复制偏移量的时候,如果发现缺少的命令在复制积压缓冲区内都可以找到,则可以通过复制积压缓冲区,将所有缺失的命令快速同步给从服务器,如果缺少的命令大于复制挤压缓冲区的大小,则主服务器还是会全量同步RDB文件。
* 复制积压缓冲区的大小:由原理可见,复制积压缓冲区的大小对掉线重连的部分重同步影响很大,那么复制积压缓冲区的大小如何选择呢?一般按(估计掉线的秒数*每秒的写命令数)来计算,为以防万一,可以将其*2。这样,掉线重连时缺少的命令,都会在复制积压缓冲区内,就可以使用快速同步了。
复制流程
- 独立的两个redis服务器,一个作为主服务器,一个作为从服务器
- 从服务器使用命令
SLAVEOF <ip> <port>
,将复制目标指向主服务器。 - 从服务器通过ip和port建立连接主服务器的套接字。这时从服务器就相当于主服务器的一个客户端
- 从服务器发送PING命令,主服务器返回PONG命令,如果不能在指定事件内返回,或者返回错误,说明连接建立的有问题,终止连接。
- 身份验证:如果从服务器设置了
masterauth
验证字段,从服务器会用它用AUTH
命令到主服务器进行身份验证。如果(1)主服务器的没有设置requirepass
字段,则验证失败。(2)如果主服务器设置了requirepass
字段,且值和从服务器的masterauth
不同,则验证失败,否则验证成功。验证失败的话,将重复建立socket连接及之后流程。 - 从服务器发送自身端口到主服务器,主服务器将其设置到从服务器对应redisClient结构体中
- 第一次复制时,从服务器发送
PSYNC ? -1
命令,不指定服务器运行ID,设置复制偏移量为-1,进行全量同步RDB,此时,主服务器也成为从服务器的客户端了。 - 主服务器返回自身运行ID等作为相应,并后台生成RDB用于同步
- 此后,进入命令传播阶段。主服务器通过复制积压缓冲区,同步写命令
- 心跳检测:从服务器默认按每秒一次的频率,向主服务器发送
REPLCONF ACK <offset>
命令,这将起到(1)检查主从的网络连接(2)确认复制偏移量,重发丢失的命令(3)辅助实现min-slaves配置(即少于多少个slaves,主服务器拒绝写命令的配置) - 如果发生掉线重连,从服务器发送
PSYNC <runId> <offset>
命令,向主服务器申请同步。 - 主服务器判断复制偏移量是否在复制积压缓冲区的缓冲范围内,如果在,返回
+CONTINUE
,执行部分重同步;如果不在,主服务器返回+FULLRESYNC <runId> <offset>
,执行全量同步