*Redis 复制指南
Redis 复制的基础是主从(leader-follower / master-replica)复制,简单易用且易于配置。它允许副本 Redis 实例成为主节点实例的精确副本。
*复制机制
系统使用三种主要机制工作:
- 正常连接时:主节点通过向副本发送命令流来保持副本更新,复制客户端写入、键过期或逐出、任何其他更改主数据集的操作效果
- 连接断开时:副本重新连接并尝试部分重新同步,仅获取断开期间错过的命令流部分
- 部分重新同步不可能时:副本请求完全重新同步。主节点需要创建所有数据的快照,发送给副本,然后继续发送命令流
Redis 默认使用异步复制,低延迟高性能,是绝大多数 Redis 用例的自然复制模式。
*复制的重要事实
- Redis 使用异步复制,副本异步确认已处理的数据量
- 一个主节点可以有多个副本
- 副本能够接受来自其他副本的连接,形成级联结构。自 Redis 4.0 起,所有子副本将接收与主节点完全相同的复制流
- Redis 复制在主节点侧非阻塞。主节点在副本执行初始同步或部分重新同步时继续处理查询
- 复制在副本侧也基本非阻塞。副本执行初始同步时可使用旧版本数据集处理查询(需在 redis.conf 中配置)
- 复制可用于扩展性(多个副本用于只读查询)或提高数据安全性和高可用性
- 可使用复制避免主节点将完整数据集写入磁盘的成本:配置主节点不持久化到磁盘,连接配置为定期保存或启用 AOF 的副本
*主节点禁用持久化时的复制安全性
强烈建议在使用 Redis 复制的设置中,主节点和副本都启用持久化。如果不可能(例如由于非常慢的磁盘导致的延迟问题),实例应配置为避免重启后自动重启。
如果主节点禁用持久化且配置自动重启,以下故障模式会擦除主节点及其所有副本的数据:
- 节点 A 作为主节点,禁用持久化,节点 B 和 C 从 A 复制
- 节点 A 崩溃,但有自动重启系统重启进程。由于禁用持久化,节点以空数据集重启
- 节点 B 和 C 将从空的 A 复制,有效销毁它们的数据副本
*复制 ID 解释
每个 Redis 主节点有一个复制 ID:标记数据集给定历史的大型伪随机字符串。每个主节点还有一个偏移量,对发送到副本的复制流的每个字节递增。
当副本连接主节点时,使用 PSYNC 命令发送其旧主复制 ID 和已处理的偏移量,主节点可以仅发送所需的增量部分。
Redis 实例有两个复制 ID(主 ID 和辅助 ID),原因是副本提升为主节点后,仍需记住过去的复制 ID,以便其他副本使用旧主复制 ID 执行部分重新同步。
*无盘复制(Diskless Replication)
通常完全重新同步需要在磁盘上创建 RDB 文件,然后从磁盘重新加载以向副本提供数据。对于慢磁盘,这可能对主节点造成很大压力。
自 Redis 2.8.18 起支持无盘复制:子进程直接通过网络将 RDB 发送给副本,不使用磁盘作为中间存储。
*配置
配置基本 Redis 复制非常简单,只需在副本配置文件中添加:
replicaof 192.168.1.1 6379
或使用 REPLICAOF 命令:
redis-cli REPLICAOF 192.168.1.1 6379
无盘复制可通过 repl-diskless-sync 配置参数启用。
*只读副本
自 Redis 2.6 起,副本默认支持只读模式,由 replica-read-only 选项控制。只读副本拒绝所有写入命令。
历史上存在一些可写副本的合法用例,但自 7.0 版本起,这些用例都已过时:
- 计算慢的 Set 或 Sorted Set 操作:改用 SUNION、ZINTER 等不存储结果的命令
- 使用 SORT 命令:改用只读命令
SORT_RO - 使用 EVAL 和 EVALSHA:改用只读版本
EVAL_RO和EVALSHA_RO
*副本认证
如果主节点通过 requirepass 设置了密码,配置副本使用密码非常简单:
config set masterauth <password>
或永久配置:
masterauth <password>
*仅当连接 N 个副本时才允许写入
自 Redis 2.8 起,可配置 Redis 主节点仅在当前至少有 N 个副本连接时才接受写入查询。
配置参数:
min-replicas-to-write <number of replicas>min-replicas-max-lag <number of seconds>
这是一种尽力而为的数据安全机制,不确保给定写入的一致性,但至少将数据丢失的时间窗口限制在给定秒数内。
*复制如何处理键的过期
Redis 使用三种主要技术使过期键的复制能够工作:
- 副本不主动过期键,而是等待主节点过期。主节点过期键时,会合成 DEL 命令传输给所有副本
- 由于主节点驱动过期,副本可能仍有逻辑上过期的键。副本使用其逻辑时钟报告键不存在,仅用于不违反数据集一致性的读操作
- Lua 脚本执行期间不执行键过期。Lua 脚本运行时,主节点的时间概念被冻结
*Docker 和 NAT 中的复制配置
当使用 Docker 或其他端口转发/NAT 时,Redis 复制需要额外注意。问题是 ROLE 命令和 INFO 输出的复制部分会显示副本用于连接主节点的 IP 地址,在 NAT 环境中可能与副本的逻辑地址不同。
自 Redis 3.2.2 起,可强制副本向主节点宣布任意 IP 和端口对:
replica-announce-ip 5.5.5.5
replica-announce-port 1234
*重启和故障转移后的部分同步
自 Redis 4.0 起,实例在故障转移后提升为主节点时,仍能与旧主节点的副本执行部分重新同步。副本记住旧复制 ID 和偏移量。
副本在温和关机并重启后,能够在 RDB 文件中存储与主节点重新同步所需的信息。需要时使用 SHUTDOWN 命令执行 save & quit 操作。
*副本上的 Maxmemory
默认情况下,副本会忽略 maxmemory(除非在故障转移后提升为主节点或手动操作)。这意味着键的逐出由主节点处理,主节点在键逐出时发送 DEL 命令给副本。
要更改此行为,允许副本不忽略 maxmemory:
replica-ignore-maxmemory no