Redis 主从模式原理
- 前言
- 搭建过程
- 全量同步
- 全量数据同步过程
- 日志分析
- redis主从同步策略
前言
redis在高并发场景下,可以称作是一个利器,一方面可以作为缓存技术,另一方面可以作为分布锁的实现方案。
对于单节点的redis来说,它会有自己的瓶颈,所以往往会采用redis集群来负载高并发。其中应用比较多的就是主从模 式,也就是采用读写分离模式。
下面来首先来探讨下,redis的主从模式的搭建,以及主从同步的原理。
搭建过程
首先采用的是一主两从的方式,(本机IP:192.168.0.2),如图:
master:192.168.0.2:7001
slave-1:192.168.0.2:7002
slave-2:192.168.0.2:7003
docker执行的命令如下:
#master 节点命令
docker run -d -p 7001:6379 --network redis --name redis-master --privileged=true -v /Volumes/bto/redis/master-slave/7001:/data redis:6.2.4
##slave-1 节点命令
docker run -d -p 7002:6379 --network redis --name redis-node-1 --privileged=true -v /Volumes/bto/redis/master- slave/7002:/data redis:6.2.4
##slave-2 节点命令
docker run -d -p 7003:6379 --network redis --name redis-node-2 --privileged=true -v /Volumes/bto/redis/master-slave/7003:/data redis:6.2.4
执行完上述命令后,执行docker ps查看运行结果,如下图:
上图可以看出,已经运行起来三个redis实例,但是此时的三个实例之间还不是主从关系,接下来建立主从,以redis-slave-1为例:
可以通过docker命令进入容器内,或者在宿主机上直接操作,这里我选择的是在宿主机上直接操作,首先链接7002实例,命令如下:
redis-cli -h 192.168.0.2 -p 7002
进入后,执行命令:
slaveof 192.168.0.2 7001
来指明它的主节点是7001。(7003同理)
【注意】从redis 5.0版本开始,该命令已经过时,应该用replicaof命令来代替slaveof。我本地用的是6.4.2,主要是习惯了slaveof。官网说明如下图:
此时连接上7001,执行如下命令,查看从节点信息:
info replication
可以看出slaves是2个节点。其中的ip=172.21.0.1是docker容器中的ip。
然后来验证下:
在master-7001上进行新增,然后看下7002,7003从节点的数据。执行结果如下图:
在7001的主节点上,新增加了{key:value} = {username:zhangsan}的数据。然后在7002和7003上分别获取,得到了相
同的结果。
尝试在从节点 7002、7003节点上进行增加操作,执行结果如图所示:
此时,redis会返回一个error结果:
READONLY You can't write against a read only replica.
就从节点是只读,无法写入。
其实上述搭建的过程很简单,没有太大的难度。接下来分析下主从数据同步的原理。
对于主从数据同步的原理呢,分为全量同步和增量同步。这篇博客主要探讨全量同步。
全量同步
在描述全量同步的过程之前,先来认识两个概念:
- replid:是实例节点的唯一表示。
- offset:数据的偏移量。
也就是说每个redis实例在启动的时候,都会有一个replid唯一表示,实际上完整叫master_replid,因为首次启动的redis时候它都是一个主节点(前提:没有在配置文件中进行配置主从),而offset可以理解成分页数据。比如:启动7001、7002后的信息如下:
上图左侧为redis:7001,右侧为redis:7002。在没有进行主从配置的时候,它们都是主节点,也就是role:master(上图红色框),同时都有各自的replid(绿色狂),其中:
replid:
7001-replid: c1e5b2e6c931b5b40bb80ec79ad07babfce7e44d
7002-replid:50452115cb9093ac6562357dfb59eb5059c33aa5
而各自的offset为:
7001-offset: 0
7002-offset:0
下面探讨下,主从数据同步的原理,如图:
上图是redis进行数据全量同步的过程,这里分成了三个阶段:
【第一阶段】:当redis实例节点7002,执行slaveof(或replicaof) 192.168.0.2 7001 命令的时候,
会向master发送自己的replid和offset给7001,然后尝试进行增量同步。当7001接收到后发现replid与
自己的不一样,说明是第一次建立主从关系,所以就会拒绝【增量同步】,而是选择【全量同步】,
并把自己(7001)的replid和offset发送给7002,7002再接收这些信息后会保存起来。
【第二阶段】:在7001向7002发送replid、offset信息的同时,会有一个【独立进程】生成rdb文件,
然后把生成好的rdb文件发送给7002。7002再接收到数据后,会把本地的数据进行清空(包括内存与rdb文件),
然后读取7001发送过来的rdb文件,并把数据加载到内存中。但是在7001(master)生成rdb的时候
是很耗时的,同时因为主节点是可以被写入的,因此在生成rdb的时候,如果有client端对master节点(7001)
进行了操作,后台进程会把这些操作命令写入到一个日志中---repl_backlog。
【第三阶段】由于在第二步生成了repl_backlog,所以master节点会采用一定策略把repl_backlog
发送给从节点,从节点再接收到日志文件后,再进一步同步本地的数据。
该三阶段就是redis主从全量同步的流程。具体这个流程是怎么来的呢?这是通过日志分析出来的,接下来看下具体的日志信息。
全量数据同步过程
- slave节点根据配置的master节点信息,连接上master节点,并向master节点发送SYNC命令;
- master节点收到SYNC命令后,执行BGSAVE命令异步将内存数据生成到rdb快照文件中,同时将生成rdb文件期间所有的写命令记录到一个缓冲区,保证数据同步的完整性;
- master节点的rdb快照文件生成完成后,将该rdb文件发送给slave节点;
- slave节点收到rdb快照文件后,丢弃所有内存中的旧数据,并将rdb文件中的数据载入到内存中;
- master节点将rdb快照文件发送完毕后,开始将缓冲区中的写命令发送给slave节点;
- slave节点完成rdb文件数据的载入后,开始执行接收到的写命令。
- 以上就是master-slave全量同步的原理,执行完上述动作后,slave节点就可以接受来自用户的读请求,同时,master节点与slave节点进入命令传播阶段,在该阶段master节点会将自己执行的写命令发送给slave节点,slave节点接受并执行写命令,从而保证master节点与slave节点的数据一致性。
日志分析
为了方便查看日志的流程,我在宿主机上进行手动进行启动7001、7002两个redis实例,并通过redis-cli连接7002,如下图:
然后我在7001中随便添加了几个数据(这里不再演示),主要是为了展示offset。
建立主从,在7002中执行命令:
replicaof 127.0.0.1 7001
输出的日志:
上屏是7001的日志,下屏是7002的日志。首先,7002开始尝试能发ping通7001,来确定是否可以建立连接 ,如下图:
然后通过后,7002开始尝试向7001请求数据,并把自己的repid、offset发给7001,如下图:
采用的是
partial resynchronization:增量同步
repid: 095911a80dc2a318ea60b16b2a0260228896ee7f(7002的repid)
offset: 1
然后7001接收到7002发送来的信息,如下图:
后台进程为26853,生成完rdb文件后,发送给7002。7002此时接收到来自7001的rdb文件,如下图:
开始全量同步,其中包括了【Flushing old data】清空旧的数据,【 Loading DB in memory】把rdb中的数据加载到内存中。这样就完成了主从同步,其实背后还有一个repl_backlog的过程,只是没有在日志中显示。
redis主从同步策略
主从刚刚连接的时候,进行全量同步;全同步结束后,进行增量同步。当然,如果有需要,slave 在任何时候都可以发起全量同步。redis 策略是,无论如何,首先会尝试进行增量同步,如不成功,要求从机进行全量同步