*Redis 复制指南

Redis 复制的基础是主从(leader-follower / master-replica)复制,简单易用且易于配置。它允许副本 Redis 实例成为主节点实例的精确副本。

*复制机制

系统使用三种主要机制工作:

  1. 正常连接时:主节点通过向副本发送命令流来保持副本更新,复制客户端写入、键过期或逐出、任何其他更改主数据集的操作效果
  2. 连接断开时:副本重新连接并尝试部分重新同步,仅获取断开期间错过的命令流部分
  3. 部分重新同步不可能时:副本请求完全重新同步。主节点需要创建所有数据的快照,发送给副本,然后继续发送命令流

Redis 默认使用异步复制,低延迟高性能,是绝大多数 Redis 用例的自然复制模式。

*复制的重要事实

  • Redis 使用异步复制,副本异步确认已处理的数据量
  • 一个主节点可以有多个副本
  • 副本能够接受来自其他副本的连接,形成级联结构。自 Redis 4.0 起,所有子副本将接收与主节点完全相同的复制流
  • Redis 复制在主节点侧非阻塞。主节点在副本执行初始同步或部分重新同步时继续处理查询
  • 复制在副本侧也基本非阻塞。副本执行初始同步时可使用旧版本数据集处理查询(需在 redis.conf 中配置)
  • 复制可用于扩展性(多个副本用于只读查询)或提高数据安全性和高可用性
  • 可使用复制避免主节点将完整数据集写入磁盘的成本:配置主节点不持久化到磁盘,连接配置为定期保存或启用 AOF 的副本

*主节点禁用持久化时的复制安全性

强烈建议在使用 Redis 复制的设置中,主节点和副本都启用持久化。如果不可能(例如由于非常慢的磁盘导致的延迟问题),实例应配置为避免重启后自动重启

如果主节点禁用持久化且配置自动重启,以下故障模式会擦除主节点及其所有副本的数据:

  1. 节点 A 作为主节点,禁用持久化,节点 B 和 C 从 A 复制
  2. 节点 A 崩溃,但有自动重启系统重启进程。由于禁用持久化,节点以空数据集重启
  3. 节点 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 操作:改用 SUNIONZINTER 等不存储结果的命令
  • 使用 SORT 命令:改用只读命令 SORT_RO
  • 使用 EVALEVALSHA:改用只读版本 EVAL_ROEVALSHA_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 使用三种主要技术使过期键的复制能够工作:

  1. 副本不主动过期键,而是等待主节点过期。主节点过期键时,会合成 DEL 命令传输给所有副本
  2. 由于主节点驱动过期,副本可能仍有逻辑上过期的键。副本使用其逻辑时钟报告键不存在,仅用于不违反数据集一致性的读操作
  3. 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