【Redis】 复制
【Redis】 复制
Metadata
title: 【Redis】 复制
date: 2023-07-09 08:44
tags:
- 行动阶段/完成
- 主题场景/数据存储
- 笔记空间/KnowladgeSpace/ProgramSpace/BasicsSpace
- 细化主题/数据存储/Redis
categories:
- 数据存储
keywords:
- 数据存储/Redis
description: 【Redis】 复制
概述
在 Redis 中,可通过 SLAVEOF 命令或配置文件中设置 slaveof 选项,让一个服务器去复制另一个服务器,被复制的为主服务器,对其复制的称为从服务器。
- Redis 2.8以前的复制功能不能高效地处理断线后重复制情况,但Redis 2.8新添加的部分重同步功能可以解决这个问题。
- 部分重同步通过复制偏移量、复制积压缓冲区、服务器运行ID三个部分来实现。
- 在复制操作刚开始的时候,从服务器会成为主服务器的客户端,并通过向主服务器发送命令请求来执行复制步骤,而在复制操作的后期,主从服务器会互相成为对方的客户端。
- 主服务器通过向从服务器传播命令来更新从服务器的状态,保持主从服务器一致,而从服务器则通过向主服务器发送命令来进行心跳检测,以及命令丢失检测。
旧版本复制功能的实现
Redis 在 2.8 以前使用旧版本复制,在短线重连后的从服务器会遇上低效的情况。
Redis 的复制功能分为同步和命令传播俩操作:
- 同步用于把从服务器的数据库状态更新至主服务器的数据库状态。
- 命令传播是在主服务器的数据库状态被修改时,导致主从数据库状态不一致时,让主从回到一致的过程。
同步
从服务器对主服务器的同步(下文以主从代替),需要向主服务器发送 SYNC 命令,具体步骤:
- 从向主发送 SYNC 命令。
- 主接收并执行 BGSAVE,后台生成 RDB 文件,并用一个缓冲区记录现在开始执行的所有写命令。
- BGSAVE 执行完毕时,主将 RDB 文件发给从,从接收并载入,更新数据库状态。
- 主将其记录在缓冲区的所有写命令发给从,从执行写命令。
命令传播
当主发生写操作时,主从同步需要通过命令传播,具体步骤:
- 主将写命令发送给从。
- 从接收并执行相同的写命令。
旧版复制功能的缺陷
旧版复制的缺陷主要体现在断线重连上,主因为网络原因中断复制,但从通过自动重连连上主,并继续复制主的时候。此时,从发送 SYNC 命令,希望将断线期间由于写操作对主的数据库状态修改同步,但 SYNC 每次都会重新生成 RDB 文件,将所有的数据库状态都写到 RDB,这就造成了资源的大量浪费。SYNC 命令对性能的损耗比较高主要表现在:
- 主执行 BGSAVE 生成 RDB 文件会消耗 CPU、内存和磁盘 I/O 资源。
- 主需要发送 RDB,消耗网络资源。
- 从接收并载入 RDB,载入期间是阻塞的无法处理命令。
因此,必须是真正有必要才调用 SYNC 命令。
新版复制功能的实现
Redis 从 2.8 开始使用 PSYNC 代替 SYNC 命令来执行同步操作。
PSYNC 有完整重同步和部分重同步的两种模式:
- 完整重同步用于初次复制的情况,与 SYNC 命令一样。
- 部分重同步用于处理断线重连后的情况,重连后,主服务器将断线期间执行的写命令发送给从服务器,从只需接收并执行这些命令。
部分重同步的执行过程:
- 从向主发送 PSYNC 命令,请求同步数据。
- 主判断后,确认需要执行部分重同步时,返回给从 +COUNTINUE。
- 主将断线期间的写命令发送给从。
部分重同步的实现
部分重同步基于三个部分实现:
- 主从服务器的复制偏移量
- 主服务器的复制积压缓冲区
- 服务器运行 ID
复制偏移量
主从都会维护一个复制偏移量,记录存储数据的字节数,当主服务器向从服务器传播 N 个字节数据时,主的复制偏移量会加 N,从接收到之后也会加 N。通过偏移量判断数据库状态是否一致。但有一个问题,就是从服务器重连后,需要执行部分还是完整重同步,这时候就需要复制积压缓冲区来帮忙判断。
复制积压缓冲区
复制积压缓冲区由主服务器维护,是固定长度的先进先出队列,默认 1M。当入队元素大于队列长度时,最先入队的元素会被弹出。主服务器在命令传播时,不仅将写命令发给从,还会将写命令入队至积压缓冲区。
复制积压缓冲区会保存最近写的命令,并为队列中的每个字节记录复制偏移量。
当从服务器重连后,发送 PSYNC 并将自己的复制偏移量也发送给主服务器,主服务器拿着复制偏移量去复制积压缓冲区找,如果存在则进行部分重同步并给从服务器发送 + CONTINUE 回复,否则进行完整重同步。
复制积压缓冲区大小应该根据实际场景的两个因素进行调整:
- 断线重连平均时间
- 主服务器平均每秒产生写命令的数据量
一般得将这两个指标相乘后再乘以 2,作为复制积压缓冲区的大小,应对大多数断线情况。
服务器运行 ID
服务器运行 ID 决定断线后执行哪种同步方式,主从都有运行 ID,是自动生成的 40 个随机十六进制字符。主从第一次复制时,从服务器会保存主服务器的 ID,断线后也会向主服务器发送这个 ID,如果不同则进行完整重同步(之前的主服务器由于某些原因连接断开,重新选举的情况);相同则部分重同步。
PSYNC 命令的实现
PSYNC 命令调用方法有两种:
- 从服务器第一次复制时,会发送
**PSYNC ? -1**
命令,请求完整重同步。 - 已经复制过的情况,向主服务器发送
**PSYNC <runid> <offset>**
命令,一个是主服务器运行 ID,一个是积压缓冲区的偏移量。
主服务器接收后有 3 种返回值:
+FULLRESYNC <runid> <offset>
:表示执行完整重同步,从服务器会将这两个变量保存。+CONTINUE
:执行部分重同步,从服务器等待缺失数据的发送。-ERR
:主服务器版本低于 2.8,执行完整重同步操作。
一次完整的主从复制过程
一次完整的复制过程可以分为设置主服务器的地址和端口、建立套接字连接、发送 PING 命令、身份验证、发送端口信息、同步、命令传播。
设置主服务器的地址和端口
当客户端向服务器发送 SLAVEOF 命令时,从服务器会将主服务器的 ip 和端口都保存后发送 OK。这是一个异步命令,所以复制工作在回复 OK 后再执行。
建立套接字连接
从服务器此时创建连接主服务器的套接字,如果套接字能成功连接,从服务器会给它关联一个处理复制工作的文件事件处理器(负责接收 RDB,传播的命令等)。主从成功连接后,主服务器会创建从服务器的客户端状态。
发送 PING 命令
从服务器在套接字连接后做的第一个工作就是发送 PING 命令,检查套接字读写状态是否正常;主服务器能否正常处理命令请求。而主服务器会根据网络状态、能够处理给出对应回复。一旦回复超时或返回错误,从服务器就会断开并重连主服务器。
身份验证
检查从服务器是否设置 masterauth,如果设置则进行身份验证。
发送端口信息
身份验证后,从服务器向主服务器发送自己监听的端口号,主服务器保存这个端口号。
同步
从服务器发送 PSYNC 命令,主从互相成为对方的客户端,都能够执行命令并回复,执行同步操作,看是完整重同步还是部分重同步。
命令传播
完成同步后,进入该阶段,主服务器将写命令发送给从服务器,从服务器接收并执行。
心跳检测
在命令传播阶段,从服务器默认 1 秒一次发送REPLCONF ACK <replication_offset>
命令给主服务器,replication_offset 是复制偏移量。这么做有 3 个作用:
- 检测主从网络状态
- 辅助实现 min-slave 选项
- 检测命令丢失
检测主从网络状态
下面分别说这三个作用。检测网络连接很好理解,如果主服务器超过一秒没受到从服务器的REPLCONF ACK
则表示连接有问题。
辅助实现 min-slave 选项
Redis 的min-slaves-to-write
和min-slaves-max-lag
可防止主服务器在不安全的情况下执行写命令。如果设置如下:
min-slaves-to-write 3
min-slaves-max-lag 10
表示从服务器数量少于 3 或 3 个从服务器延迟大于等于 10s 时,主服务器拒绝写命令。
检测命令丢失
通过发送的偏移量,主服务器会判断命令是否有丢失,如果丢失,就从积压缓冲区里找到并补发。
注:Redis2.8 之前版本并不会注意到丢失数据,所以保持主从数据一致性最好使用以上版本。