Redis 主从模式原理

  • 前言
  • 搭建过程
  • 全量同步
  • 全量数据同步过程
  • 日志分析
  • redis主从同步策略


前言

redis在高并发场景下,可以称作是一个利器,一方面可以作为缓存技术,另一方面可以作为分布锁的实现方案。

  对于单节点的redis来说,它会有自己的瓶颈,所以往往会采用redis集群来负载高并发。其中应用比较多的就是主从模 式,也就是采用读写分离模式。

swarm redis 主从 redis主从模式原理_redis


下面来首先来探讨下,redis的主从模式的搭建,以及主从同步的原理。

搭建过程

首先采用的是一主两从的方式,(本机IP:192.168.0.2),如图:

swarm redis 主从 redis主从模式原理_数据_02


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查看运行结果,如下图:

swarm redis 主从 redis主从模式原理_数据同步_03


上图可以看出,已经运行起来三个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。官网说明如下图:

swarm redis 主从 redis主从模式原理_redis_04


此时连接上7001,执行如下命令,查看从节点信息:

info replication

swarm redis 主从 redis主从模式原理_docker_05


可以看出slaves是2个节点。其中的ip=172.21.0.1是docker容器中的ip。

然后来验证下:

在master-7001上进行新增,然后看下7002,7003从节点的数据。执行结果如下图:

swarm redis 主从 redis主从模式原理_redis_06

在7001的主节点上,新增加了{key:value} = {username:zhangsan}的数据。然后在7002和7003上分别获取,得到了相
同的结果。

尝试在从节点 7002、7003节点上进行增加操作,执行结果如图所示:

swarm redis 主从 redis主从模式原理_redis_07


此时,redis会返回一个error结果:

READONLY You can't write against a read only replica.

就从节点是只读,无法写入。

其实上述搭建的过程很简单,没有太大的难度。接下来分析下主从数据同步的原理。

对于主从数据同步的原理呢,分为全量同步和增量同步。这篇博客主要探讨全量同步。

全量同步

在描述全量同步的过程之前,先来认识两个概念:

  1. replid:是实例节点的唯一表示。
  2. 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

下面探讨下,主从数据同步的原理,如图:

swarm redis 主从 redis主从模式原理_docker_08


上图是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主从全量同步的流程。具体这个流程是怎么来的呢?这是通过日志分析出来的,接下来看下具体的日志信息。

全量数据同步过程

  1. slave节点根据配置的master节点信息,连接上master节点,并向master节点发送SYNC命令;
  2. master节点收到SYNC命令后,执行BGSAVE命令异步将内存数据生成到rdb快照文件中,同时将生成rdb文件期间所有的写命令记录到一个缓冲区,保证数据同步的完整性;
  3. master节点的rdb快照文件生成完成后,将该rdb文件发送给slave节点;
  4. slave节点收到rdb快照文件后,丢弃所有内存中的旧数据,并将rdb文件中的数据载入到内存中;
  5. master节点将rdb快照文件发送完毕后,开始将缓冲区中的写命令发送给slave节点;
  6. slave节点完成rdb文件数据的载入后,开始执行接收到的写命令。
  7. 以上就是master-slave全量同步的原理,执行完上述动作后,slave节点就可以接受来自用户的读请求,同时,master节点与slave节点进入命令传播阶段,在该阶段master节点会将自己执行的写命令发送给slave节点,slave节点接受并执行写命令,从而保证master节点与slave节点的数据一致性。

日志分析

为了方便查看日志的流程,我在宿主机上进行手动进行启动7001、7002两个redis实例,并通过redis-cli连接7002,如下图:

swarm redis 主从 redis主从模式原理_数据同步_09


然后我在7001中随便添加了几个数据(这里不再演示),主要是为了展示offset。

建立主从,在7002中执行命令:

replicaof 127.0.0.1 7001

swarm redis 主从 redis主从模式原理_数据_10


输出的日志:

swarm redis 主从 redis主从模式原理_数据库_11


上屏是7001的日志,下屏是7002的日志。首先,7002开始尝试能发ping通7001,来确定是否可以建立连接 ,如下图:

swarm redis 主从 redis主从模式原理_redis_12


然后通过后,7002开始尝试向7001请求数据,并把自己的repid、offset发给7001,如下图:

swarm redis 主从 redis主从模式原理_docker_13


采用的是

partial resynchronization:增量同步
repid: 095911a80dc2a318ea60b16b2a0260228896ee7f(7002的repid)
offset: 1

然后7001接收到7002发送来的信息,如下图:

swarm redis 主从 redis主从模式原理_docker_14


后台进程为26853,生成完rdb文件后,发送给7002。7002此时接收到来自7001的rdb文件,如下图:

swarm redis 主从 redis主从模式原理_redis_15


开始全量同步,其中包括了【Flushing old data】清空旧的数据,【 Loading DB in memory】把rdb中的数据加载到内存中。这样就完成了主从同步,其实背后还有一个repl_backlog的过程,只是没有在日志中显示。

redis主从同步策略

主从刚刚连接的时候,进行全量同步;全同步结束后,进行增量同步。当然,如果有需要,slave 在任何时候都可以发起全量同步。redis 策略是,无论如何,首先会尝试进行增量同步,如不成功,要求从机进行全量同步