什么是主从复制
持久化保证了即使redis服务重启也不会丢失数据,因为redis服务重启后会将硬盘上持久化的数据恢复到内存中,但是当redis服务器的硬盘损坏了可能会导致数据丢失,不过通过redis的主从复制机制就可以避免这种单点故障,如下图:
说明:
- 主redis中的数据有两个副本(replication)即从redis1和从redis2,即使一台redis服务器宕机其它两台redis服务也可以继续提供服务。
- 主redis中的数据和从redis上的数据保持实时同步,当主redis写入数据时通过主从复制机制会复制到两个从redis服务上。
- 只有一个主redis,可以有多个从redis。
- 主从复制不会阻塞master,在同步数据时,master 可以继续处理client 请求
- 一个redis可以即是主又是从,如下图:
主从复制架构
主从配置:
修改从服务器上的 redis.conf 文件:
# slaveof <masterip> <masterport>
# 表示当前【从服务器】对应的【主服务器】的IP是192.168.10.135,端口是6379。
slaveof 192.168.10.135 6379
主从复制的实现原理
- Redis 的主从同步,分为全量同步和增量同步。
- 只有从机第一次连接上主机是全量同步。
- 断线重连有可能触发全量同步也有可能是增量同步( master 判断 runid 是否一致)。
全量同步
Redis 的全量同步过程主要分三个阶段:
同步快照阶段:Master创建并发送快照RDB给 Slave,Slave载入并解析快照。Master同时将此阶段所产生的新的写命令存储到复制积压缓冲区。
同步写缓冲阶段:Master向Slave同步存储在复制积压缓冲区的写操作命令。
同步增量阶段:Master向Slave同步写操作命令。
增量同步
Redis增量同步主要指Slave完成初始化后开始正常工作时,Master发生的写操作同步到 Slave`的过程。
通常情况下,Master 每执行一个写命令就会向Slave发送相同的写命令,然后Slave接收并执行。
主从复制原理细节
PSYNC命令执行赋值时的同步操作
PSYNC命令具有完整重同步(full resynchronization 全量同步)和部分重同步(partial resynchronization 增量同步)两种模式:
完整重同步用于初次复制情况:通过让主服务器创建并发送RDB文件,以及向从服务器发送保存在缓冲区里面的写命令来进行同步。
部分重同步用于处理断线后重复制情况:当从服务器在断线后重新连接主服务器时,如果条件允许,主服务器可以讲主从服务器连接断开期间执行的写命令发送给从服务器,从服务器只要接受并执行这些写命令,就可以将数据库更新至主服务器当前所处的状态。
部分重同步的实现
部分重同步由一下3个部分构成:
主服务器的复制偏移量(replication offset)和从服务器的复制偏移量。
主服务器的复制积压缓冲区(replication backlog)。
服务器的运行ID(run ID)。
复制偏移量:
主服务器和从服务器会分别维护一个复制偏移量。
主服务器每次向从服务器传播N个字节的数据时,就将自己的复制偏移量的值加N。
从服务器每次收到主服务器传播来的N各字节数据时,就将自己的复制偏移量的值加N。
如下展示主服务器和它的3个从服务器偏移量的变化
初始状态,主从服务器偏移量的值为10086:
此时,客户端向主服务器发送 33字节的数据,那么主服务器的赋值偏移量将更新为10086+33=10119。
三个从服务器接收到主服务器传播的数据之后,也会将复制偏移量更新为10119。
从上述例子可以看出,主从服务器的偏移量相同,那么说明主从服务器处于一致状态。
下边演示一个主从服务器的偏移量不一致的例子,模拟从服务器断线的场景:
从服务器A在断线后立即重启连接主服务器,并且成功,那么接下来,从服务器A向主服务器发送PSYNC命令,报告从服务器A当前的复制偏移量offset为10086,此时,主服务器会检查从服务器发来的offset是否在复制积压缓冲区中,进而决定是进行完整重同步还是部分重同步。
复制积压缓冲区
复制积压缓冲区由主服务器维护的一个固定长度的先进先出队列,默认大小1MB。
主服务器进行命令传播时,不仅会将写命令发送给所有从服务器,还会将写命令入队到复制积压缓冲区中,如下图所示:
复制积压缓冲区中保存着一部分最近传播的写命令,并且复制积压缓冲区会为队列中的每个字节记录相应的复制偏移量。
主服务器决定对从服务器进行何种同步操作的判断:
从服务器重新连上主服务器时,通过PSYNC命令将自己的复制偏移量offset发送给主服务器。
如果offset偏移量之后的数据仍然在复制积压缓冲区中,那么主服务器将对从服务器执行部分重同步操作。
如果offset偏移量之后的数据已经不在复制积压缓冲区中了,那么主服务器将对从服务器执行完整重同步操作。
服务器运行ID
除了复制偏移量和复制积压缓冲区之外,部分重同步还需要用到服务器运行ID(run ID):
每个Redis服务器,不论主服务器还是从服务器,都有自己的运行ID。
运行ID在服务器启动时自动生成,由40个随机的十六进制字符组成。
当从服务器对主服务器进行初次复制时,主服务器会将自己的运行ID传送给从服务器,而从服务器则会将这个运行ID保存起来。
根据运行ID判断是完整重同步还是部分重同步:
从服务器断线并重新连上一个主服务器时,从服务器将向当前的主服务器发送之前保存的运行ID(初次复制时主服务器发送给从服务器的ID)。
如果从服务器保存的运行ID和当前连接的主服务器的运行ID相同,说明从服务器断线之前复制的就是当前连接的这个主服务器,此时主服务器可以继续尝试执行部分重同步操作。
如果从服务器保存的运行ID和当前连接的主服务器运行ID不同,那么说明从服务器断线之前复制的主服务器不是当前连接的这个主服务器,主服务器将对从服务器执行完整重同步操作。
PSYNC命令两种场景下的不同调用
如果从服务器之前没有复制过任何主服务器,或之前执行过SLAVEOF NO ONE命令,那么从服务器在开始一次新的复制时,将向从服务器发送PSYNC ? -1命令,主动请求主服务器进行完整重同步。
如果从服务器已经复制过某个主服务器,那么从服务器在开始新的复制时,将向主服务器发送PSYNC <runid> <offset>命令:runid是上一次复制的主服务器的运行ID,offset则是从服务器当前的复制偏移量。主服务器会根据收到的PSYNC命令中runid、offset参数来判断判断执行哪种同步操作。
主服务器收到PSYNC命令时,会向从服务器返回以下三种回复中的一种:
- 主服务器返回+FULLRESYNC <runid> <offset>回复,表示主服务器将与从服务器执行完整重同步操作(runid是主服务器的运行ID,从服务器会将这个ID保存起来,在下一次发送PSYNC时使用;offset是主服务器当前的复制偏移量,从服务器会将该值作为自己的初始化偏移量)。
- 主服务器返回+CONTINUE回复,那么表示主服务器将与从服务器执行部分重同步操作,从服务器只要等着主服务器将自己缺少的那部分数据发过来就可以了。
- 如果主服务器返回-ERR回复,表示主服务器的版本低于Redis 2.8,他识别不了PSYNC命令,从服务器将向主服务器发送SYNC命令,并与主服务器执行完整同步操作。
PSYNC命令执行完整重同步和部分重同步可能遇到的情况如下图:
一个将完整重同步和部分重同步串起来: