redis通过搭建主从机,主机负责写操作,并将数据同步给从机,然后在和从机共同承担读操作可以大大提高redis的响应速度。
当我们在从机配置文件中配置replicaof(老版本是salveof)加上主机ip时,从机启动后则会主动向这个ip请求建立tcp连接。成功建立tcp连接后,从机会像主机发送一条指令psync ? -1(具体意思后面说),接收到这条指令后,主机返回+FULLRESYNC <replid> <offset>(replid和offset也后面说),并开始全量复制。
全量复制:全量复制是主机生成rdb文件(rdb文件是redis服务器的一次快照,保存当前服务器的全部数据,具体可以看我的另一篇文章redis持久化存储,rdb快照文件,aof文件),并将rdb文件发送给从机,从机在根据rdb文件恢复和主机一样的数据。
主机在创建要发送给从机的rdb文件的时刻之后,所有的写操作命令都会存入复制缓冲区,并且通过tcp连接将写操作命令传给从机,从机执行这个指令来保证和主机数据同步。整个过程如下图所示
在连接期间主机会周期性向从机发送ping指令,从机回复pong指令已确认从机存活,当既定时间没有收到从机回复的pong时则标记从机为断连,释放TCP连接,从机恢复后会重新尝试和主机连接。
在redis2.8版本之前,主从机的复制请求是通过sync完成的,sync无论从机是否保存部分主机数据,都会让主机进行全量复制,但假如从机只是短暂的暂停一下,并没有和主机数据相差太多呢?此时进行全量复制则是性能的极大浪费(全量复制复制全部redis的数据及其耗时),而2.8版本之后则出现了psync指令,主机在向复制缓冲区插入写操作指令时会维护一个offset,offset会记录主机向复制缓冲区插入的多少字节的指令,而从机也会维护一个offset,记录接受了多少字节的指令,从机在进行持久化存储时也会将offset存入rdb文件或aof文件( 具体可以看我的另一篇文章redis持久化存储,rdb快照文件,aof文件),这样在从机短暂宕机重启后会向主机发送psync <replid> <offset>指令,主机会根据从机的offset和复制缓冲区的offset来对从机进行增量复制(增量复制就是将0复制缓冲区中从机保存的offset往后的指令传给从机进行数据同步),但复制缓冲区的空间是有限的(默认是1mb),当从机宕机时间过长,从机保存的offset和主机保存的offset的差值已经大于1mb,那么复制缓冲区内的指令则不足以让从机和主机数据同步,这时则必须执行全量复制(比如说从机的offset保存的是1mb偏移量,而主机保存的是2.5mb偏移量,由于缓冲区只能保存1mb数据,所以此时复制缓冲区只有1.5mb偏移量到2.5mb偏移量的数据,1mb偏移量到1.5偏移量的数据则无法通过增量恢复来同步数据)。
如果从机宕机期间主机也宕机了呢?或者由于某些原因,这个ip和端口已经换了一个全新的redis服务了呢?这将导致虽然从机保存的offset和主机保存的offset的差值可能小于1mb,但此时主机的复制缓冲区内部保存的指令已经不是之前的指令了,这时replid的作用就凸显出来了,replid是主机创建时生成的一个独一无二的复制id,在第一次建立连接时主机就通过+FULLRESYNC <replid> <offset>指令将replid传给了从机,当从机宕机后回复重连,请求回复数据时发送的指令psync <replid> <offset>也是携带着这个replid,主机则会验证这个replid,只有当replid相同,并且从机保存的offset和主机保存的offset的差值小于1mb时,才会进行增量复制。
了解了offset和replid后,我们便可以解释第一段的指令,首先从机第一次完成TCP连接时,并不知道replid和offset,所以用?代替replid,-1代替offset,代表请求全量复制,也就是psync ? -1指令。而主机在第一次全量复制时也通过+FULLRESYNC <replid> <offset>将replid和offset发送给从机保存。