主从模式是redis最基础的高可用模式,通过建立一主多从的关系,实现数据的备份保存保存功能。主从模式的结构如下图所示。
在redis主从模式中,主节点对外提供数据的读写服务,从节点保持对主节点的数据备份,并可以对外提供数据读取服务。
心跳
在主从关系中,从节点每秒中会向主节点上报信息,维护主从之间的心跳关系。发送的命令为REPLCONF ACK offset,上报自己的信息以及数据的offset。
主节点会维护所有从节点的信息
slave0:ip=192.168.0.11,port=6379;state=online;offset=198817,lag=0
slave1:ip=192.168.0.12,port=6379;state=online;offset=198815,lag=1
其中lag表述的是主从之间的心跳延迟,表示上次心跳正常实在几秒之前。
主从数据同步
主从关系需要实现主节点数据备份同步到从节点,实现原理如下
在2.8版本之前,使用sync
在redis2.8版本之前,redis使用sync同步机制,同步过程如下图所示
数据同步的步骤如下
- 通过“slaveof ip port”命令建立主从关系后,从节点向主节点发送sync执行,请求同步
- 主节点执行bgsave命令,创建rbd快照文件,同时在创建过程中新接收的指令保存到缓冲区中
- rbd快照文件制作完成,主节点将rbd文件发送给从节点
- 从节点接收到rbd快照文件后,通过rbdLoad命令,加载快照文件,从节点通过快照恢复的方式加载数据到内存
- 主节点在发送完rbd快照文件后,将缓冲区内的信息传输给从节点
- 从节点接收主节点发送的缓冲区数据,并更新到内存,至此第一次同步完成。
- 后续主节点每次接收一次数据的命令,都直接发送命令给从节点执行,保持主从数据的一致(该阶段称为命令传播)
sync的同步阶段优缺点如下:
优点:
1.自动同步,建立主从关系后自动进行数据同步
2.非阻塞行同步,同步过程中通过bgsave方法制作rbd快照文件,不会在主节点产生任务阻塞的情况(快照rbd文件制作的save方法会阻塞)
存在问题:
1.每次同步都是全量同步,如果主从节点发生网络抖动导致失联,在再一次恢复连接后,无论是否发生数据变化,都进行的是全量同步。
2.无法应对主节点切换的场景
因上述文件,redis在2.8版本之后,引入psync同步机制,实现部分同步,解决问题1
psync部分同步(2.8版本后)
为了实现部分同步,redis引入三个新概念
- 主从redis服务器的复制偏移量(replication offset)
复制偏移量从0开始技术
主节点每次接收到N个字节的数据,就将自己offset的数值+N
从节点每次接收到主节点发送的N个字节数据,将本地节点的offset数值+N。
传输过程如下图所示,如果某个从节点和主节点断开连接,从节点的偏移量保持不变。
主从的同步关系正常,则主从节点的offset基本保持一致
- 主服务器的复制积压缓冲区(replication backlog),
redis主节点维护一个固定长度的先进先出队列,保存数据,默认1M,可以修改,公式second*write_size_per_second
在新版本中,redis主节点不仅仅会将数据保存到redis的数据区及发送给从节点,还同时会保存在复制积压缓冲区,这样复制积压缓冲区就会保存当前一部分最近时间传播的命令,并且缓冲区会按照偏移量记录每个偏移量所对应的数值,缓冲区的结构是通过偏移量+数值的形式保存
- 主服务器的运行ID
1. 无论是redis的主节点还是从节点,都会保存一个runid,runid是服务启动时自动生成,由40个16进制字符表示。
2. 从节点会保存主节点的runid
这样psync实现部分同步的过程如下图所示。
同步场景一:
当第一次通过slaveof建立主从关系,从节点发送psync?-1,主节点回复+fullresync runid offset,告知从节点主节点的runid和复制偏移量,触发全量同步
同步场景二
当之后断网重连,从节点恢复连接后发送psync runid offset命令给主节点,其中runid表示从节点在断网之前同步的主节点runid,offset表示从节点当前的复制偏移量。
主节点在接收到从节点发送的指令后,进行如下判断
- 判断命令中runid是否和自己的runid是否一致
a. 两者一致,表示从节点断网之前主节点是自己,进行offset校验
b. 两者不一致,表示从节点之前的主节点不是自己,返回+fullresync runid offset,执行全量同步 - 判断命令中offset自己的积压缓冲区数据是否冲突,主要判断上报命令的offset是否在主节点的复制积压缓冲区范围内。
a. 如果从节点上报的offset在主节点复制积压缓冲区的偏移量范围之内,表示从节点在断网期间和主节点不一致的数据可以单独通过传递复制积压缓冲区中的数据即可恢复,便可以进行部分同步,回复从节点+continue,等待主节点发送复制积压缓冲区中的数据,进行部分同步
例如上报的offset是1001,主节点的复制积压缓冲区范围是990-1300,这样,就是将缓冲区对应的1002-1300数据发送给从节点即可。
b. 如果从节点上报的offset不在主节点的复制积压缓冲区范围之内,代表从节点缺失数据较多,无法单独通过复制积压缓冲区进行恢复。回复从节点+fullresync runid offset,执行全量同步。
例如上报的offset是1001,主节点的复制积压缓冲区范围是1100-1300,这种情况下无法仅通过传输缓冲区数据实现数据一致的同步过程,便只能进行全量同步。
整个校验过程如下图所示
psync的优缺点如下
优点:
1. 可以实现部分同步,解决断网重连后的全同步导致的资源浪费和消耗
存在问题
1. 无法应对主节点切换情况,当主节点切换后,从节点给新主节点上报的runid是原先主节点的id,新主节点判断不一致,便直接进行完整同步,并没有进行更深入的判断
为解决主节点切换情况下的部分同步功能,redis4.0版本后引入的psync2.0,有效解决该问题
psync2.0解决主节点切换情况下的数据同步
psync2.0通过增加repid2和持久化runid来解决主从切换后从节点和新主节点的同步必然是全同步问题。
- 由于runid是系统每次启动服务后自动生成,导致每次不一致,redis4.0后在做rbd持久化的时候,会将runid保存
- 增加repid2,保留任何一个节点上一个主节点的runid。
数据结构如下所示
struct redisServer {
...
/* Replication (`master`) */
char replid[CONFIG_RUN_ID_SIZE+1]; /* My current replication ID. */
char replid2[CONFIG_RUN_ID_SIZE+1]; /* replid inherited from `master`*/
}
这样当发生主从切换,新的主节点中的replid2会保留他在成为主节点之前作为从节点的数据来源主节点的id。
从节点和新主节点进行第一次psync runid offset判断时,主不仅仅会将上报的runid和自己当前id进行判断,还会和replid2判断,如果两者一致,表示这两个节点的数据曾同步过同一个主节点的数据,这样继续进行offset的判断,进一步判断是否需要进行部分同步。
redis数据同步的cap问题
CAP【一致性(Consistency)、可用性(Availability)、分区容错性(Partition tolerance)】理论认为:在分布式系统中,多个节点之间只能满足CP或AP,即强一致性和高可用是不能同时满足的。Redis的主从同步是AP的,具体对高可用的强度要求,可用通过在redis.conf配置,即有至少有多少个slaves存在和至多多少秒内没响应,则才执行写请求,否则报错,配置与说明如图:默认为关闭这个特性,即master始终接收客户端写请求。
redis主从模式优缺点
优点
1、架构简单、部署方便、维护成本低
2、数据自动同步,保证从节点的数据备份
缺点
1、无法对节点状态进行监测
2、无法自动进行故障恢复,需要手动干预,无法商用生产
3、主节点写入数据压力较大