Redis的持久化技术AOF 和 RDB保证了即使在服务器重启的情况下也不会丢失数据(或少量损失),不过,由于数据都是存储在一台服务器上,
- 如果服务器发生了宕机,由于数据恢复是需要点时间,那么这个期间是无法服务新的请求的;
- 如果这台服务器的硬盘出现了故障,可能数据就都丢失了。
为了避免这种情况,最好的办法是将数据备份到其他服务器上,让这些服务器也可以对外提供服务,这样即使有一台服务器出现了故障,其他服务器依然可以继续提供服务。
Redis 提供了主从复制模式
,来避免上述的问题。
这个模式可以保证多台服务器的数据一致性,且主从服务器之间采用的是「读写分离」的方式。
主服务器可以进行读写操作,当发生写操作时自动将写操作同步给从服务器,而从服务器一般是只读,并接受主服务器同步过来写操作命令,然后执行这条命令。
原理
Redis 主从复制大体可以分为三个阶段:
- 建立连接阶段: 建立链接、协商同步
- 数据同步阶段:主服务器同步数据给从服务器
- 命令传播阶段:主服务器发送新写操作命令给从服务器
第一次同步
我们可以使用 replicaof(Redis 5.0 之前使用 slaveof)命令形成主服务器和从服务器的关系。
# 服务器 B 执行这条命令 B为从服务器
replicaof <服务器 A 的 IP 地址> <服务器 A 的 Redis 端口号>
建立连接
执行了replicaof
命令后,从服务器就会给主服务器发送 psync
命令,表示要进行数据同步。
psync 命令包含两个参数,分别是主服务器的 runID 和复制进度 offset。
-
runID
,每个 Redis 服务器在启动时都会自动生产一个随机的 ID 来唯一标识自己。当从服务器和主服务器第一次同步时,因为不知道主服务器的 run ID,所以将其设置为 "?"。 -
offset
,表示复制的进度,第一次同步时,其值为 -1。
主服务器收到 psync 命令后,会用 FULLRESYNC 作为响应命令返回给对方。
并且这个响应命令会带上两个参数:主服务器的 runID 和主服务器目前的复制进度 offset。从服务器收到响应后,会记录这两个值。
FULLRESYNC 响应命令的意图是采用全量复制的方式,也就是主服务器会把所有的数据都同步给从服务器。
所以,第一阶段的工作是为了全量复制做准备。
那具体怎么全量同步呀呢?我们可以往下看第二阶段。
数据同步
-
接着,主服务器会执行 bgsave 命令来生成 RDB 文件,然后把文件发送给从服务器。
-
从服务器收到 RDB 文件后,会先清空当前的数据,然后载入 RDB 文件。
这里有一点要注意,主服务器生成 RDB 这个过程是不会阻塞主线程的,也就是说 Redis 依然可以正常处理命令。
但是这期间的写操作命令并没有记录到刚刚生成的 RDB 文件中,这时主从服务器间的数据就不一致了。
- 那么为了保证主从服务器的数据一致性,主服务器会将在 RDB 文件生成后收到的写操作命令,写入到 replication buffer 缓冲区里。
命令传播
在主服务器生成的 RDB 文件发送后,然后将 replication buffer 缓冲区里所记录的写操作命令发送给从服务器,然后从服务器重新执行这些操作
拓扑结构
- 一主一从:最简单的拓扑结构,一般适用于没有太大的并发场景。当 master 宕机时,slave 提供故障转移支持
- 一主多从:适用于并发量较大的场景,一般都是读多写少,客户端可以将读命令发送到 salve 节点分担 master 节点压力。实现读写分离架构,当然也保证了高可用。但是该架构需要避免复制风暴。
- 树状结构:slave 节点除了在 master 复制数据,也可以在其他 slave 节点复制数据。主要是通过引用复制中间层,降低 master 节点的负载和需要传送给从节点的数据量,这种架构可以避免复制风暴,但是延长了数据一致性。
复制风暴
在前面的分析中,我们可以知道主从服务器在第一次数据同步的过程中,主服务器会做两件耗时的操作:生成 RDB 文件和传输 RDB 文件。
主服务器是可以有多个从服务器的,如果从服务器数量非常多,而且都与主服务器进行全量同步的话,就会带来两个问题:
-
由于是通过 bgsave 命令来生成 RDB 文件的,那么主服务器就会忙于使用 fork() 创建子进程,如果主服务器的内存数据非大,在执行 fork() 函数时是会阻塞主线程的,从而使得 Redis 无法正常处理请求;
-
传输 RDB 文件会占用主服务器的网络带宽,会对主服务器响应命令请求产生影响。
总结
主从复制共有三种模式:全量复制
、基于长连接的命令传播
、增量复制
。
主从服务器第一次同步的时候,就是采用全量复制,此时主服务器会两个耗时的地方,分别是生成 RDB 文件和传输 RDB 文件。为了避免过多的从服务器和主服务器进行全量复制,可以把一部分从服务器升级为「经理角色」,让它也有自己的从服务器,通过这样可以分摊主服务器的压力。
第一次同步完成后,主从服务器都会维护着一个长连接,主服务器在接收到写操作命令后,就会通过这个连接将写命令传播给从服务器,来保证主从服务器的数据一致性。
如果遇到网络断开,增量复制就可以上场了,不过这个还跟 repl_backlog_size 这个大小有关系。
如果它配置的过小,主从服务器网络恢复时,可能发生「从服务器」想读的数据已经被覆盖了,那么这时就会导致主服务器采用全量复制的方式。所以为了避免这种情况的频繁发生,要调大这个参数的值,以降低主从服务器断开后全量同步的概率。