推荐阅读
如果Redis的读写请求量很大,那么单个实例很有可能承担不了这么大的请求量,如何提高Redis的性能呢?你也许已经想到了,可以部署多个副本节点,业务采用读写分离的方式,把读请求分担到多个副本节点上,提高访问性能。要实现读写分离,就必须部署多个副本,每个副本需要实时同步主节点的数据。
Redis也提供了完善的主从复制机制,使用非常简单的命令,就可以构建一个多副本节点的集群。
同时,当主节点故障宕机时,我们可以把一个副本节点提升为主节点,提高Redis的可用性。可见,对于故障恢复,也依赖Redis的主从复制,它们都是Redis高可用的一部分。
这篇文章我们就来介绍一下Redis主从复制流程和原理,以及在复制过程中有可能产生的各种问题。
构建主从复制集群
假设我们现在有一个节点A,它经过写入一段时间的数据写入后,内存中保存了一些数据。
此时我们再部署一个节点B,需要让节点B成为节点A的数据副本,并且之后与节点A保持实时同步,如何做呢?
Redis提供了非常简单的命令:slaveof
。我们只需要在节点B上执行以下命令,就可以让节点B成为节点A的数据副本:
slaveof 节点A_host:节点A_port
节点B就会自动与节点A建立数据同步,如果节点A的数据量不大,等待片刻,就能看到节点B拥有与节点A相同的数据,同时在节点A上产生的数据变更,也会实时同步到节点B上。
通过这样简单的方式,我们可以非常方便地构建一个master-slave
集群,业务可以在master上进行写入,在slave上读取数据,实现读写分离,提高访问性能。
那么主从节点的复制是如何进行的?下面我们就来分析其中的原理。
主从复制流程
为了方便下面讲解,我们这里把节点A叫做master节点,节点B叫做slave节点。
当我们在slave上执行slaveof
命令时,这个复制流程会经过以下阶段:
- slave发送
psync $runid $offset
给master,请求同步数据 - master检查slave发来的
runid
和offset
参数,决定是发送全量数据还是部分数据 - 如果slave是第一次与master同步,或者master-slave断开复制太久,则进行全量同步
- master在后台生成RDB快照文件,通过网络发给slave
- slave接收到RDB文件后,清空自己本地数据库
- slave加载RDB数据到内存中
- 如果master-slave之前已经建立过数据同步,只是因为某些原因断开了复制,此时只同步部分数据
- master根据slave发来的数据位置
offset
,只发送这个位置之后的数据给slave - slave接收这些差异数据,更新自己的数据,与maser保持一致
- master根据slave发来的数据位置
- 之后master产生的写入,都会传播一份给slave,slave与master保持实时同步
下面分别介绍全量同步和部分同步的详细流程。
全量同步
当我们在节点B上执行slaveof
命令后,节点B会与节点A建立一个TCP连接,然后发送psync $runid $offset
命令,告知节点A需要开始同步数据。
这两个参数的具体含义如下:
runid
:master节点的唯一标识offset
:slave需要从哪个位置开始同步数据
什么是runid
?在启动Redis实例时,Redis会为每个实例随机分配一个长度为40位的十六进制字符串,用来标识实例的唯一性,也就是说,runid
就是这个实例的唯一标识。
由于是第一次同步,slave并不知道master的runid
,所以slave会r发送psync ? -1
,表示需要全量同步数据。
master在收到slave发来的psync
后,会给sla