主从复制
目录
- 主从复制
- 第一次同步
- 基于长连接的命令传播
- 增量复制
- 心跳检测
为了避免单点故障,多个服务器保存同一份数据,这样即使有一台服务器出现了故障,其他服务器依然可以继续提供服务。
Redis 提供了主从复制模式
来实现,该模式保证了多台服务器的数据一致性,主从服务器之间采用的是 读写分离
的方式。
也就是说,所有的数据修改只在主服务器上进行,然后将最新的数据同步给从服务器,这样就使得主从服务器的数据是一致的。
第一次同步
我们可以使用replicaof
(Redis 5.0之前使用slaveof)命令形成主服务器和从服务器的关系。
主从服务器的第一次同步的过程可分为三个阶段,如图:
- 执行
replicaof
命令后,从服务器就会给主服务器发送psync
命令,表示进行同步。psync
命令包含两个参数,分别是主服务器的runlD
和复制进度offset
。 -
runID
,每个 Redis 服务器在启动时都会自动生产一个随机的 ID 来唯一标识自己。当从服务器和主服务器第一次同步时,因为不知道主服务器的 run ID,所以将其设置为 "?"。 -
offset
,表示复制的进度,第一次同步时,其值为 -1。
- 主服务器用
FULLRESYNC
作为响应命令返回给对方,并且会带上两个参数,主服务器的 runID 和主服务器目前的复制进度 offset。
FULLRESYNC
响应命令的意图是采用 全量复制 的方式,就是主服务器会把所有的数据都同步给从服务器。 - 接着,主服务器会执行
bgsave
命令来生成RDB文件,然后把文件发送给从服务器。从服务器收到RDB
文件后,会先清空当前的数据,然后载入RDB
文件。
在生成RDB
的这个过程不会阻塞主线程,在这期间的写操作命令会写到replication buffer
缓冲区里。 - 在生成的
RDB
文件发送后,将replication buffer
缓冲区里记录的写操作命令发送给从服务器,然后执行这些操作
基于长连接的命令传播
主从服务器在完成第一次同步后,双方会维护一个TCP 链接来保证主从服务器的数据一致性。
后续主服务器可以通过这个连接将写操作命令传播给从服务器,然后从服务器执行该命令,使得与主服务器的数据库状态相同。
而且这个连接是长连接,目的是避免频繁的TCP连接和断开带来的性能开销。
增量复制
从服务器对主服务器的复制可以分为两种情况:
- 初次复制:从服务器以前没有复制过任何主服务器,或者从服务器当前要复制的主服务器和上一次复制的主服务器不同。
- 断线后重复制:在Redis 2.8 之前,处于命令传播阶段的主从服务器因为网络原因而中断了命令同步,但又重连上了,从服务器会和主服务器重新进行一次全量复制,但是这样效率非常低。
所以在Redis 2.8 开始,全量复制用于初次复制的情况。网络断开又恢复后,主从服务器会采用 增量复制 的方式继续同步,也就是只会把网络断开期间主服务器接收到的写操作命令,同步给从服务器。
主要步骤:
- 从服务器恢复网络后,发送
psync
命令,此时offset
参数不是-1; - 主服务器收掉命令后,用
CONTINUE
响应命令告诉从服务器接下来采用增量复制的方式同步数据; - 然后主服务将主从服务器断线期间,所执行的写命令发送给从服务器,然后从服务器执行这些命令。
那么主服务器怎么知道要将哪些增量数据发送给从服务器呢?
原因是有这两个东西:复制偏移量(replication offset) 和 复制积压缓冲区(repl_backlog_buffer) .
- 复制偏移量:执行复制的主服务器和从服务器分别会维护一个复制偏移量,用来标记各自写或读取到的位置。如果两者偏移量不同,说明主从服务器并未处于一致状态。
- 复制积压缓冲区:由主服务器维护的一个环形缓冲区,默认大小为1M,。在进行命令传播时,不仅会把写命令发送给从服务器,还会将写命令写到积压缓冲区中。当缓冲区写满后,主服务器继续写入的话,就会覆盖之前的数据。
在从服务器重新连上主服务器时,从服务器会通过psync
命令将自己的复制偏移量offset
发送给主服务器:
- 如果
offset
偏移后的数据仍然存在于复制积压缓冲区中,那么就执行增量复制的同步操作。 - 相反执行全量复制。
所以如果复制积压缓冲区配置的过小,主从服务器网络恢复时,可能发生「从服务器」想读的数据已经被覆盖了,那么这时就会导致主服务器采用全量复制的方式。所以为了避免这种情况的频繁发生,要调大这个参数的值,以降低主从服务器断开后全量同步的概率。
心跳检测
在命令传播阶段,从服务器默认会以每秒一次的频率,向主服务器发送命令:
REPLCONF ACK <replication_offset>
replication_offset
是从服务器当前的复制偏移量。
主要有三个作用:
- 检测主从服务器的网络连接状态
- 辅助实现
min-slaves
选项 - 检测命令丢失