Redis 高可靠性,有两层含义:一是数据尽量少丢失,二是服务尽量少中断。AOF 和 RDB 的持久化保证了数据尽量少丢失,而对于服务尽量少中断,Redis 的做法就是增加副本冗余量,将一份数据同时保存在多个实例上。即使有一个实例出现了故障,需要过一段时间才能恢复,其他实例也可以对外提供服务,不会影响业务使用。
Redis 实现高可靠有三种部署模式:主从模式,哨兵模式,集群模式。
主从模式中,主从节点之间采用的是读写分离的方式。主节点负责读写操作,从节点只负责读操作。从节点的数据来自主节点,实现原理就是主从复制机制
主从复制包括全量复制,增量复制两种。一般当从节点(slave)第一次启动连接主节点(master),就采用全量复制,全量复制主要包括三个阶段:
第一阶段,从节点和主节点建立起连接,并告诉主节点即将进行同步,主节点确认回复后,主从节点间就可以开始同步了。
具体来说,从节点给主节点发送 psync 命令,表示要进行数据同步,主节点根据这个命令的参数来启动复制。psync 命令包含了主节点的 runID 和复制进度 offset 两个参数。第一次复制时, runID 设为“?”,offset设为 -1。
主节点收到 psync 命令后,会用 FULLRESYNC 响应命令带上两个参数:主节点 runID 和主节点目前的复制进度 offset,返回给从节点。从节点收到响应后,会记录下这两个参数。
第二阶段,主节点将所有数据同步给从节点。从节点收到数据后,在本地完成数据加载。这个过程依赖于内存快照生成的 RDB 文件。
具体来说,主节点执行 bgsave 命令,生成 RDB 文件,接着将文件发给从节点。从节点接收到RDB 文件后,会先清空当前数据库,然后加载 RDB 文件。
在主节点将数据同步给从节点的过程中,主节点不会被阻塞,仍然可以正常接收请求。为了保证主从节点的数据一致性,主节点会在内存中用专门的 replication buffer,记录RDB 文件生成之后收到的所有写操作。
第三个阶段,主节点会把第二阶段执行过程中新收到的写命令,再发送给从节点。
具体来说,当主节点完成 RDB 文件发送后,就会把此时 replication buffer 中的修改操作发给从节点,从节点再重新执行这些操作。
这样一来,主从节点就实现同步了。
主从级联模式分担全量复制时的主节点压力
一次全量复制中,对于主节点来说,需要完成两个耗时的操作:生成 RDB 文件和传输 RDB 文件。
如果从节点数量很多,而且都要和主节点进行全量复制的话,就会导致主节点忙于 fork 子进程生成 RDB 文件,进行数据全量同步。fork 这个操作会阻塞主线程处理正常请求,从而导致主节点响应应用程序的请求速度变慢。此外,传输 RDB 文件也会占用主节点的网络带宽,同样会给主节点的资源使用带来压力。
我们可以通过“主 - 从 - 从”模式将主节点生成 RDB 和传输 RDB 的压力以级联的方式分散到从节点上。
简单来说,我们在部署主从集群的时候,可以手动选择一个从节点(比如选择内存资源配置较高的从节点),用于级联其他的从节点。其他的从节点不用再和主节点进行交互了,只要和级联的从节点进行写操作同步就行了,这就可以减轻主节点上的压力。
一旦主从节点完成了全量复制,它们之间就会一直维护一个网络连接,主节点会通过这个连接将后续收到的命令操作再同步给从节点,这个过程也称为基于长连接的命令传播,可以避免频繁建立连接的开销。
主从节点间网络断了怎么办?
在 Redis 2.8 之前,如果主从节点在命令传播时出现了网络闪断,那么,从节点就会和主节点重新进行一次全量复制,开销非常大。
从 Redis 2.8 开始,网络断了之后,主从节点会采用增量复制的方式继续同步。当主从节点断连后,主节点你会把断连期间收到的写操作命令,写入 replication buffer,同时也会把这些操作命令也写入 repl_backlog_buffer 这个缓冲区。
repl_backlog_buffer 是一个环形缓冲区,主节点会记录自己写到的位置( master_repl_offset),从节点则会记录自己已经读到的位置( slav