Redis 主从模式
主从库如何同步
全量同步
命令传播
增量同步
单点部署的服务无论服务本身设计的多完美,只要服务所在的机器宕掉了,都是白扯。
在要求高可用性的系统设计上,都会遵守一个核心准则:冗余。
也就是说某个节点挂掉后,还有其他节点可以继续提供服务。
对 存储系统 设计冗余要考虑如何保证 数据的一致性(所有节点的数据要一致)。
如果冗余节点也可以接收写请求,那在数据同步的问题上将变得非常复杂,所以,存储系统的冗余节点一般有两种使用方法:
1、只作为一个数据备份,不接收任何读写请求;
2、采用读写分离模式,冗余节点可以接收读请求,但不处理写请求。
以上两种方式都保证所有的写请求只发生在一个节点,只要将变更的数据同步到冗余节点即可。
Redis 采用 一主多从 的方式提供冗余副本,同时主从节点采用 读写分离方式,不仅可以保证数据一致性,从节点还可以分担主节点的负载(读请求可以在从节点处理)。·
Redis 提供命令 replicaof 命令 来建立主从关系:
# ip 和 port 为主节点地址端口,执行该命令的节点自动成为从节点
replicaof ip port
当一个节点执行 replicaof 命令后,表明这个节点是新增的一个 从节点, 此时,主节点要把所有的数据都复制给这个从节点,即第一次执行 全量同步;
全量同步之后,从节点就可以处理读请求了,此外,从节点会和主节点会维护一个 长连接 用于命令同步,因为只有主节点可以处理写命令,所以主节点还要把写命令传播到从节点来保证数据一致性,此为 命令传播;
思考一个问题,如果在命令传播阶段发生主从断连,命令传播就被会被中断,等到主从节点重新连接上之后,要怎么恢复主从一致呢?
在 Redis 2.8 之前,主节点要重新执行一次全量同步。但是仔细想想,其实主从节点只是相差了断连时间内的一点写请求,所以在 Redis 2.8 开始,Redis 实现了 增量同步,即只将主从断连期间的写命令同步给从节点。
接下来看下每个阶段详细过程:
runID: 每个 Redis 实例启动时都会自动生成的一个随机 ID,用来唯一标记这个实例;
offset: 同步偏移量。
1、同步协商:
首先从节点发送 psync {runID} {offset} 命令给主节点,表示要进行数据同步。因为是首次连接,从节点还没有主节点的信息,因此 runID = ? ,第一次复制时,offset = -1;
然后主节点回复命令 FULLRESYNC {runID} {offset},此时,runID 为主节点的 ID值,offset 为主节点当前的复制进度。
2、数据同步:
主节点会执行 bgsave 命令生成此时全量数据的 RDB 文件,并发送给从节点,然后从节点要清空数据库,最后加载 RDB 文件。
以上整个过程主节点是不会阻塞的(bgsave 命令会 fork 子进程来生成 RDB 文件,执行 fork 时会阻塞),那这段时间产生的写命令在 RDB 文件中是不存在的,因此为了保证主从节点的数据一致性,还需要将这段时间的写命令都保存起来,这就是以上 replication buffer 的作用。(回忆一下 AOF 重写,是不是似曾相识)
3、缓冲命令同步:
完成 RDB 文件的发送后,主节点继续将 replication buffer 中的命令发送到从节点,从节点将这些命令也执行一遍,这时主从节点的数据就一样啦。
至此,主从节点已经完成同步,在此之后,主节点会与从节点之间维护一个长连接,用来同步后续主节点的数据操作,即 基于长连接的命令传播。
但是这存在一个问题,如果某个时间点主从节点之间的连接被中断了,就无法再进行命令传播,也就导致主从节点数据的不一致。既然从节点也可以作为一个读节点来使用,所以数据不一致肯定是不行的,来看看解决方案:
增量同步顾名思义,指的是同步在网络断连期间,主节点上发生且没有同步到从节点的数据操作;需要注意的是,一个主节点可能有多个从节点,而每个从节点的都有可能与主节点发生断连,且发生断连时的时间也不尽一致,因此,差异数据对于每个从节点而言可能是不一致的,那主节点就要区分与不同从节点之间的差异数据,我们直接来看看 Redis 的实现方案。
首先,在命令传播阶段,只要存在从节点,那么发送到主节点的写命令除了要同步到从节点以外,还会发送到一块 循环缓存区,这个缓存区也叫 repl_backlog_buffer;
其次,主节点使用偏移量 master_repl_offset 来记录自己写到的位置,而每个从节点也各自使用自己的偏移量 slave_repl_offset 来记录自己读到的位置;有了这两个偏移量,就可以从 repl_backlog_buffer 获取到增量数据了。
现在,我们来看下增量同步的过程:
1、从节点恢复连接后,向主节点发送 psync 命令,记录的偏移量 slave_repl_offset 将作为参数一起发送;
2、主节点收到命令后,向从节点发送 continue 命令,表示接下来要进行的是 增量同步;
3、主节点会根据自己的偏移量 master_repl_offset 和从节点的偏移量 slave_repl_offset 得到差异数据,然后发送给从节点。
需要注意的是,因为 repl_backlog_buffer 是一个循环缓冲区,那么就有可能出现数据被覆盖的情况,而如果发生了覆盖,就只能再次进行全量同步了。所以,要特别关注一下循环缓存区的大小 repl_backlog_size 这个参数,一般可以用以下方法来预估该值:
repl_backlog_size = (主节点写入命令速度 * 操作大小 - 主从节点间网络传输命令速度 * 操作大小) * 2