Redis - 主从模式
单机模式下的 Redis 一旦出现单机故障,就会导致整个缓存服务不可用,这对于业务系统来说是不可接受的,因此最简单的处理方式就是进行备份,当主要服务宕机时,能有备份顶上。这就是主从模式。
Redis 也有主从模式,主节点提供读写服务,从节点同步主节点数据,并提供读服务。那么有了主从节点,就必然要解决一个问题,主从节点之间如何进行同步,Redis 中是这样做的。
首次同步
当我们启动多个 Redis 实例的时候,它们相互之间就可以通过 replicaof(Redis 5.0 之前使用 slaveof)命令形成主库和从库的关系,从节点首次进行主从同步的时候,会向主节点发送 psync 命令,表示要进行数据同步。psync 命令包含了主库的 runID 和复制进度 offset 两个参数。
runID,是每个 Redis 实例启动时都会自动生成的一个随机 ID,用来唯一标记这个实例。当从库和主库第一次复制时,因为不知道主库的 runID,所以将 runID 设为“?”。(与 HTTP 中的序号有异曲同工之妙)
offset,此时设为 -1,表示第一次复制。主库收到 psync 命令后,会用 FULLRESYNC 响应命令带上两个参数:主库 runID 和主库目前的复制进度 offset,返回给从库。从库收到响应后,会记录下这两个参数。
然后主节点就会通过 bgsave 命令生成一个 RDB 文件,记录下当前当前主节点的所有数据,并将 RDB 文件传递给从节点。从节点接收到了 RDB 文件后,就会清空自己当前的数据库,然后加载 RDB 文件。这是因为从库在通过 replicaof 命令开始和主库同步前,可能保存了其他数据。为了避免之前数据的影响,从库需要先把当前数据库清空。
在主库将数据同步给从库的过程中,主库不会被阻塞,仍然可以正常接收请求。否则,Redis 的服务就被中断了。但是,这些请求中的写操作并没有记录到刚刚生成的 RDB 文件中。为了保证主从库的数据一致性,主库会在内存中用专门的 replication buffer,记录 RDB 文件生成后收到的所有写操作。最后,主库会把生成 RDB 文件并发送给重库中新收到的写命令,再发送给从库。具体的操作是,当主库完成 RDB 文件发送后,就会把此时 replication buffer 中的修改操作发给从库,从库再重新执行这些操作。这样一来,主从库就实现同步了。
一旦主从库完成了全量复制,它们之间就会一直维护一个网络连接,主库会通过这个连接将后续陆续收到的命令操作再同步给从库,这个过程也称为基于长连接的命令传播,可以避免频繁建立连接的开销。
网络断联后再恢复
当网络出现阻塞或者其余情况导致连接断开,在主从断开连接的时机,主节点会维护了一个循环缓冲队列:repl_backlog_buffer,用于记录断联时主节点接收到的命令,然后当网络连接恢复后,从节点会将当前同步的位移传递给主节点,如果位移还在 repl_backlog_buffer 中,就可以进行增量同步;因为 repl_backlog_buffer 是一个环形缓冲区,所以在缓冲区写满后,主库会继续写入,此时,就会覆盖掉之前写入的操作。如果从库的读取速度比较慢,就有可能导致从库还未读取的操作被主库新写的操作覆盖了,这会导致主从库间的数据不一致,此时主从之间就会重新进行一个全量同步。
因此,为了避免主从之间频繁进行全量同步,我们可以调整 repl_backlog_size 这个参数。
缓冲空间的计算公式是:缓冲空间大小 = 主库写入命令速度 * 操作大小 - 主从库间网络传输命令速度 * 操作大小。
在实际应用中,考虑到可能存在一些突发的请求压力,我们通常需要把这个缓冲空间扩大一倍,即 repl_backlog_size = 缓冲空间大小 * 2。
通过调大这个参数,可以减少从库在网络断连时全量复制的风险。
评论