Redis 主从同步分为两个过程:全量同步和增量同步
全量同步一般发生在 Slave 的初始化阶段,也就是 Slave 节点的启动阶段。具体的步骤如下(最好对照着刚刚启动的日志来看):
- 从服务器连接主服务器(Log: Connecting to MASTER 192.168.238.1:6379)
- 从服务器发送 SYNC 命令到主服务器(Log: MASTER <-> SLAVE sync started)
- 主服务器接收到 SYNC 命名后,开始执行 BGSAVE 命令生成 RDB 文件并使用缓冲区记录此后执行的所有写命令(Log: Starting BGSAVE for SYNC with target: disk)
- 主服务器 BGSAVE 执行完后,递增地将文件发送到从服务器,并在发送期间继续记录被执行的写命令(Log: Synchronization with slave 192.168.238.129:6379 succeeded)
- 从服务器收到快照文件后丢弃所有旧数据,载入收到的快照(Log: MASTER <-> SLAVE sync: Flushing old data)
- 从服务器将快照载入内存(Log: MASTER <-> SLAVE sync: Loading DB in memory)
- 主服务器快照发送完毕后开始向从服务器发送缓冲区的写命令
- 从服务器完成对快照的载入,开始接收主服务器发送的写命令请求,并执行写命令。
- 至此,一次全量同步完成
增量同步是指 Redis 在主从模式已经正常工作的情况下,主服务器将写操作同步到从服务器的过程 增量复制的原理当主服务器每执行一个写命令,就会将该命令发送给所有的从服务器,从服务器接收到命令之后立即执行。步骤:
- 主节点执行 BGSAVE 生成全量镜像的 RDB 文件,是如何来工作的?
- 首先 BGSAVE 指令是用来在后台异步保存当前数据库的数据到磁盘,不会阻塞主节点的进程,当执行 BGSAVE 后,会立即返回 OK, 然后 Redis fork 出一个新子进程 来专门生成全量镜像文件的工作,将数据保存到磁盘后,子进程退出。
- 当主节点在 BGSAVE 的过程中,又有新的写请求到来,主节点怎么工作?这些写数据怎么保存?
- 根据 1 的细节,我们知道主节点会有一个新的子进程来单独处理全量镜像的生成工作,那么主节点父进程可以继续来接收新的请求。这里需要注意的是,当发生 fork 的时候,操作系统会采用写时复制(copy-on-write)策略 即 fork 函数发生的那一刻,父子进程共享同一块内存数据,当父进程接收到写操作时,操作系统会复制一份数据以保证子进程的数据不受影响。所以新生产的 RDB 文件存储的是执行 fork 那一刻的数据。
- 主服务执行 BGSAVE 命令如果磁盘不够生成 rdb 文件数据的大小怎么办?不能同步了吗?
- 可以采用 无磁盘复制 技术; 通常来讲,一个完全重同步需要在磁盘上创建一个 RDB 文件,然后加载这个文件以便为从服务器发送数据。如果使用比较低速或者容量较小的磁盘,这种操作会给主服务器带来较大的压力。
- Redis从2.8.18版本开始尝试支持无磁盘的复制。使用这种设置时,子进程直接将 RDB 文件通过网络发送给从服务器,不使用磁盘作为中间存储。
- 这里需要注意:使用硬盘备份传送是多个从节点排队等待传送,也就是说传送是串行的,而无磁盘复制技术是主节点在传送之前会等待一段时间(可以配置,以秒为单位),希望等待多个从节点都到达后,并行传送。
- 主从同步的过程中,从服务器是阻塞的吗?
- 主从同步的过程中,也不会阻塞从服务器。当从服务器进程初始同步时,会使用旧的数据继续提供查询服务。当然这个也可以在配置文件修改。但是,需要注意的是,并不是整个过程都是不阻塞的,当从服务器接受到快照文件,需要删除旧的快照并加装新的数据集到内存,在这个短暂的 时间内,从服务器会阻塞连接进来的请求。
- 当主从节点断开复制之后,从节点的数据会删除吗?
- 如果需要断开复制,从节点执行命令:
slaveof no one
从节点不会删除已有的数据,只是不再接受主节点新的数据变化
- 如果节点宕掉了或者主从重新连接是如何重新恢复同步机制的?
- 在 Redis 2.8 之前同步机制和 Redis2.8 之后的同步机制不太一样。下面分开来介绍
复制的情况分为两种情况:
- 初次同步
- 初次同步即从节点在复制当前主节点之前,没有复制过任何的主节点,或者是从节点要复制的和上一次要复制的主节点不同。对于初次复制来说,旧版的复制功能没有任何问题,能够完成同步功能。
- 断线后重同步
- 当处于增量复制阶段的时候,主从节点的连接因为网络原因中断了复制,但从节点又重新连接上了主节点,并继续复制主服务器。重同步的机制如下:
- 从节点重新连接上了主节点
- 从节点向主节点发送 SYNC 的命令
- 主节点接受到 SYNC 的命令,执行 BGSAVE 命令开始生成 rdb 文件, 并使用缓冲区来记录所有的写命令。注意这里生成的并不是全量的文件,是从未同步成功的key开始到当前的key的数据文件。
- 主节点向从节点发送 rdb 文件
- 从节点接受到 rdb 文件,将数据载入
- 主从节点数据恢复一致
从上面可以看出,基本上和初次同步没什么不同。都要进行 SYNC 的操作, 而 SYNC 是一个非常耗费资源的操作:
- 每次都要进行 BGSAVE 操作,会耗费服务器大量的 CPU、内存和磁盘
- 将生成的 rdb 文件通过网络发送给从服务器,这个操作会耗费主从服务器大量的网络带宽和流量,会对主节点响应命令请求的时间产生影响
- 从节点接受到 rdb 文件之后会将数据载入内存,这个时候从服务器是阻塞的,没有办法处理请求
为了解决旧版的断线后重同步的缺陷,新版的 Redis 使用 PSYNC 命令来代替 SYNC 来执行复制时的同步操作。PSYNC 的工作模式有两种**:**完整重同步 和 部分重同步。
- 完整重同步
- 完整重同步和旧版的初次同步一样,这里不再详细说明
- 部分重同步
- 部分重同步则主要用于处理断线后重连的情况:当从节点连接到主节点之后,如果 条件允许,则主节点可以将主从节点连接断开期间执行的写命令发送给从节点,从节点接受到这些命令后执行,就可以将数据更新至一致
部分重同步主要由三部分组成:
- 主节点的 复制偏移量 和从节点的 复制偏移量
- 主节点的 复制积压缓冲区
- 节点的运行 ID
主从节点都会维护一个复制偏移量:
- 主节点每次向从节点发送 N 个字节的数据时,就将自己的复制偏移量加 N
- 从节点接受到 N 个字节数据时,也将自己的复制偏移量加 N
当发生断线重连的时候,从节点发送 PSYN 命令给主节点,主节点会将当前的复制偏移量发送给从节点,通过对比主从节点的复制偏移量就很容易知道主从节点的数据是否处于一致。
复制积压缓冲区是由主节点维护的一个固定长度的队列,默认大小是 1 M。当主节点在增量同步的时候,不仅会将命令发送给从节点,同时也会将命令写入到复制积压缓冲区的队列里。 当队列满时,会自动弹出队首的数据,所以复制积压缓冲区会保存一部分最近发送的写命令。
那么当从节点连上主节点之后,从节点也会将自己的 复制偏移量发送给主节点,主节点会根据偏移量的不同来决定采取何种的重同步机制:
- 当从节点的复制偏移量还在复制积压缓冲区里,那么主节点将对从节点执行部分重同步,即只发送丢失的写命令
- 相反,如果从节点的复制偏移量已经不在复制积压缓冲区里,那么主节点必须执行完整的重同步
注意:可以根据需要来调整复制积压缓冲区的大小。如果主服务器平均每秒产生 1 MB的写数据,而从节点断线之后平均要 5 秒才能重新连接上主节点,那么复制积压缓冲区的大小就不能低于5MB
无论是主从节点都会在服务启动时自动生成生成一个运行ID,由40个随机字符串组成,例如:9di9b28df834rf09iab5e345fbbab667ydoopp2b3
- 当初次同步时,主节点会将自己的运行ID发送给从节点,而从节点会将这个运行ID保存下来。即从节点保存了主节点的的运行ID
- 当从节点重新连接上主节点时,从节点会向主节点发送保存的主节点运行ID:
- 主节点判断从节点发送过来的保存的运行ID和自己的运行ID相同,则证明节点断线之前就是连接的这个主节点,主节点就可以继续尝试去部分重同步。
- 相反,如果运行ID不相同,则说明从节点断线之前复制的主节点不是当前的主节点,主节点将直接进行完整重同步操作。
主从节点连接断掉之后,主节点是如何知道的? 心跳监测机制,在增量同步的过程中,从节点默认会以每秒一次的频率向主节点发送命令:REPLCONF ACK <replication_offset>,命令的作用在于:
- 检测主从服务器的网络连接状态
- 检测命令是否丢失
- 如果主服务器超过 1 秒钟没有收到从节点发来的 REPLCONF ACK 命令,那么主节点就知道与从节点之间的连接出现问题了。
- 如果使用主从同步的集群模式,强烈建议开启主节点的持久化机制,但这并不是必须的,如果不开启持久化机制,也应当关闭主节点实例自动重启,原因如下:
- 当主节点不持久化,从节点正常复制主节点的数据,当主出现了一个崩溃,这时候如果 Redis 自动重启服务,因为没有持久化,节点重启后就只有一个空的数据集。
- 其他的从节点依旧从主节点复制数据,所以最终从节点的数据也会为空。
- 同一个 Master 可以部署多个 Slave
- Slave 还可以接受其他的 Slave 的连接和同步,即所谓的 级联结构。有效的减轻 Master 的压力
- 主从同步期间,主从节点均是非阻塞。不影响服务的查询和写入
- 可以很好的实现读写分离的架构,系统的伸缩性得到提高
- 主机的宕机会非常严重,导致整个数据不一致的问题。
- 全量的复制的过程中,必须保证主节点必须有足够的内存。若快照的文件过大,还会对集群的服务能力产生影响。