Redis 复制

Redis 如何通过复制支持高可用性和故障转移

Redis 堆栈 Redis 社区版

在 Redis 复制的基础(不包括 Redis Cluster 或 Redis Sentinel 作为附加层提供的高可用性功能)中,有一个易于使用和配置的领导追随者(主副本)复制。它允许副本 Redis 实例成为主实例的精确副本。每次链接断开时,副本都会自动重新连接到 master,并且无论 master 发生什么情况,都会尝试成为它的精确副本。

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

  1. 当主实例和副本实例连接良好时,主实例通过向副本发送命令流来保持副本更新,以复制由于以下原因对数据集的影响:客户端写入、密钥过期或逐出、任何其他更改主数据集的作。
  2. 当 master 和 replica 之间的链接中断时,由于网络问题或因为在 master 或 replica 中检测到超时,replica 会重新连接并尝试继续进行部分重新同步:这意味着它将尝试只获取在断开连接期间错过的部分命令流。
  3. 当无法进行部分重新同步时,副本将要求完全重新同步。这将涉及一个更复杂的过程,其中 master 需要创建其所有数据的快照,将其发送到副本,然后在数据集更改时继续发送命令流。

Redis 默认使用异步复制,即低延迟和 高性能,是绝大多数 Redis 的自然复制模式 使用案例。但是,Redis 副本异步确认数据量 他们定期与主服务器一起接收。所以 master 不会每次都等待 对于要由副本处理的命令,但是,如果需要,它知道什么 replica 已处理 what 命令。这允许使用可选的同步复制。

客户端可以使用 这WAIT命令。然而WAIT只能确保存在 指定数量的已确认副本,则不会 将一组 Redis 实例转换为具有强一致性的 CP 系统:已确认 在故障转移期间,写入仍可能丢失,具体取决于确切的配置 Redis 持久性。但是, WAIT 将故障事件后丢失写入的可能性大大降低到特定的难以触发的故障模式。

有关更多信息,您可以查看 Redis Sentinel 或 Redis Cluster 文档 关于高可用性和故障转移。本文档的其余部分主要介绍 Redis 基本复制的基本特性。

有关 Redis 复制的重要事实

  • Redis 使用异步复制,异步副本到主节点确认处理的数据量。
  • 一个 master 可以有多个副本。
  • 副本能够接受来自其他副本的连接。除了将多个副本连接到同一个 master 之外,副本还可以以类似级联的结构连接到其他副本。从 Redis 4.0 开始,所有子副本都将从 master 接收完全相同的复制流。
  • Redis 复制在主端是非阻塞的。这意味着当一个或多个副本执行初始同步或部分重新同步时,主服务器将继续处理查询。
  • 复制在副本端也基本上是非阻塞的。当副本执行初始同步时,它可以使用旧版本的数据集处理查询,前提是您在 redis.conf 中将 Redis 配置为执行此作。否则,您可以将 Redis 副本配置为在复制流关闭时向客户端返回错误。但是,在初始同步后,必须删除旧数据集,并且必须加载新数据集。副本将在此短暂的窗口内阻止传入连接(对于非常大的数据集,可能长达几秒钟)。从 Redis 4.0 开始,您可以配置 Redis,以便在不同的线程中删除旧数据集,但加载新的初始数据集仍将在主线程中进行并阻止副本。
  • 复制既可用于可扩展性,也可以将多个副本用于只读查询(例如,可以将慢速 O(N)作卸载到副本),或者仅用于提高数据安全性和高可用性。
  • 您可以使用复制来避免让主服务器将完整数据集写入磁盘的成本:一种典型的技术涉及配置主服务器redis.conf要完全避免持久保存到磁盘,请连接配置为不时保存或启用 AOF 的副本。但是,必须谨慎处理此设置,因为重新启动的主数据库将以空数据集开始:如果副本尝试与其同步,则副本也将被清空。

master 关闭持久性时复制的安全性

在使用 Redis 复制的设置中,强烈建议使用 在 Master 和 Replica 中打开持久性。如果无法做到这一点, 例如,由于磁盘速度非常慢导致延迟问题,实例应 配置为避免在重新启动后自动重新启动

为了更好地理解为什么关闭了持久性的 master 配置为 自动重启很危险,请检查以下 data 的失败模式 已从 master 及其所有副本中擦除:

  1. 我们有一个设置,节点 A 充当主节点,关闭持久性,节点 B 和 C 从节点 A 复制。
  2. 节点 A 崩溃,但它有一些自动重启系统,可以重启进程。但是,由于持久性已关闭,因此节点会以空数据集重新启动。
  3. 节点 B 和 C 将从节点 A 复制,节点 A 为空,因此它们将有效地销毁其数据副本。

当 Redis Sentinel 用于高可用性时,同时关闭持久性 在主服务器上,以及进程的自动重启是危险的。例如,主服务器可以以足够快的速度重新启动,以便 Sentinel 不会检测到故障,从而发生上述故障模式。

每次数据安全很重要,并且使用复制时配置了无持久性的 master 时,都应禁用实例的自动重启。

Redis 复制的工作原理

每个 Redis 主节点都有一个复制 ID:它是一个大的伪随机字符串 标记数据集的给定历史记录。每个 master 还采用一个偏移量,该偏移量 复制流的每个字节的增量 sent to replicas,以使用新的更改更新副本的状态 修改数据集。即使没有副本,复制偏移量也会递增 实际上是 connected 的,所以基本上每个给定的对:

Replication ID, offset

标识主数据集的确切版本。

当副本连接到 master 时,它们使用PSYNC命令发送 他们的旧主复制 ID 和他们到目前为止处理的偏移量。这边 主服务器可以只发送所需的增量部分。但是,如果没有 主缓冲区中有足够的积压,或者如果副本引用 history (replication ID) (复制 ID),则完全重新同步 发生:在这种情况下,副本将从头开始获得数据集的完整副本。

以下是完全同步的更多细节:

主服务器启动后台保存过程以生成 RDB 文件。同时,它开始缓冲从 Client 端收到的所有新写入命令。后台保存完成后,主服务器将数据库文件传输到副本,副本将其保存在磁盘上,然后将其加载到内存中。然后 master 会将所有缓冲的命令发送到副本。这是以命令流的形式完成的,格式与 Redis 协议本身相同。

您可以通过 telnet 自己尝试。连接到 Redis 端口,同时 服务器正在执行一些工作,并发出SYNC命令。您将看到一个 bulk transfer 的 intent 命令,然后 master 接收到的每个命令都会重新发出 在 telnet 会话中。实际上SYNC是不再使用的旧协议 较新的 Redis 实例,但仍存在以实现向后兼容性:它确实 不允许部分重新同步,所以现在PSYNC

如前所述,当主副本链接由于某种原因关闭时,副本能够自动重新连接。如果主服务器收到多个并发副本同步请求,它将执行单个后台保存以为所有请求提供服务。

复制 ID 说明

在上一节中,我们说过,如果两个实例具有相同的复制 ID 和 replication offset 时,它们的数据完全相同。但是它很有用 了解复制 ID 到底是什么,以及为什么实例实际上具有 两个复制 ID:主 ID 和辅助 ID。

复制 ID 基本上标记数据集的给定历史记录。每次 实例作为 master 从头开始重启,或者将副本提升为主实例, 为此实例生成新的复制 ID。连接到 主服务器将在握手后继承其复制 ID。所以两个实例 由于它们持有相同的数据,因此存在关系,但 可能在不同的时间。它是用作逻辑时间的偏移量 了解给定历史记录(复制 ID)谁持有最新的 数据集。

例如,如果两个实例 A 和 B 具有相同的复制 ID,但一个 偏移量为 1000,偏移量为 1023,这意味着第一个缺少确定性 命令。这也意味着 A,通过仅应用少量 命令可能会达到与 B 完全相同的状态。

Redis 实例之所以有两个复制 ID,是因为副本 被提升为主服务器。故障转移后,提升的副本需要 仍记得其过去的复制 ID,因为此类复制 ID 是前任主人的那个。这样,当其他副本将同步时 对于新的 Master,他们将尝试使用 旧主复制 ID。这将按预期工作,因为当副本 提升为主 ID 时,它会将其辅助 ID 设置为其主 ID,并记住 是发生此 ID 切换时的偏移量。稍后它将选择一个新的随机数 replication ID,因为新的历史记录开始。处理新副本时 connecting,则 master 会将其 ID 和 offset 与当前的 ID 和辅助 ID(为安全起见,最多为给定的偏移量)。简而言之,这意味着 在故障转移后,连接到新提升的 master 的副本没有 以执行完全同步。

如果您想知道为什么提升为 master 的副本需要更改其 故障转移后的复制 ID:旧主服务器可能仍为 由于某些网络分区而作为 master 工作:保留相同的 复制 ID 会违反以下事实:任何 两个随机实例意味着它们具有相同的数据集。

无盘复制

通常,完全重新同步需要在磁盘上创建一个 RDB 文件。 然后从磁盘重新加载相同的 RDB,以向副本提供数据。

对于慢速磁盘,这对 master 来说可能是一个非常紧张的作。 Redis 版本 2.8.18 是第一个支持无盘的版本 复制。在此设置中,子进程直接发送 通过网络 RDB 连接到副本,而不使用磁盘作为中间存储。

配置

配置基本的 Redis 复制很简单:只需将以下行添加到副本配置文件中:

replicaof 192.168.1.1 6379

当然,您需要将 192.168.1.1 6379 替换为您的主 IP 地址(或 hostname) 和 port 的 Hostname 的 Pod Pod 的 Pod Pod或者,您也可以调用REPLICAOF命令和 master host 将启动与副本的同步。

还有一些参数可用于调整所采用的复制积压 在 master 执行部分重新同步。查看示例redis.conf随 Redis 发行版一起提供,以了解更多信息。

可以使用repl-diskless-sync配置 参数。开始传输以等待更多副本 arrive 在第一个由repl-diskless-sync-delay参数。请参考示例redis.confRedis 发行版中的文件 了解更多详情。

只读副本

从 Redis 2.6 开始,副本支持默认启用的只读模式。 此行为由replica-read-only选项,并且可以在运行时使用CONFIG SET.

只读副本将拒绝所有写入命令,因此无法由于错误而写入副本。这并不意味着该功能旨在将副本实例公开给 Internet,或者更普遍地向存在不受信任的客户端的网络公开,因为像DEBUGCONFIG仍处于启用状态。Security (安全) 页面介绍了如何保护 Redis 实例。

您可能想知道为什么可以恢复只读设置 并具有可作为写入作目标的副本实例。 答案是,可写副本仅出于历史原因而存在。 使用可写副本可能会导致 master 和 replica 不一致,因此不建议使用可写副本。 要了解在哪些情况下这可能是一个问题,我们需要了解复制的工作原理。 通过将常规 Redis 命令传播到副本来复制主服务器上的更改。 当密钥在主服务器上过期时,这将作为 DEL 命令传播。 如果密钥存在于主服务器上,但已被删除、过期或与主服务器相比在副本上具有不同的类型,则对从主服务器传播的 DEL、INCR 或 RPOP 等命令的反应将与预期不同。 propagated 命令可能会在副本上失败或导致不同的结果。 为了最大限度地降低风险(如果您坚持使用可写副本),我们建议您遵循以下建议:

  • 不要写入 master 上使用的可写副本中的键。 (如果您无法控制写入 master 的所有 Client 端,则很难保证这一点.)

  • 在升级正在运行的系统中的一组实例时,不要将实例配置为可写副本作为中间步骤。 通常,如果要保证数据一致性,如果实例可以提升为主副本,请不要将其配置为可写副本。

从历史上看,有一些用例被认为是可写副本的合法。 从版本 7.0 开始,这些用例现在都已过时,可以通过其他方式实现相同的效果。 例如:

  • 计算慢速 Set 或 Sorted set作,并使用 Desired 等命令将结果存储在临时本地键中SUNIONSTOREZINTERSTORE. 相反,请使用返回结果而不存储结果的命令,例如SUNIONZINTER.

  • 使用SORT命令(由于可选的 STORE 选项,该命令不被视为只读命令,因此不能用于只读副本)。 相反,请使用SORT_RO,这是一个只读命令。

  • EVALEVALSHA也不被视为只读命令,因为 Lua 脚本可能会调用 write 命令。 相反,请使用EVAL_ROEVALSHA_RO其中 Lua 脚本只能调用只读命令。

如果副本和主副本重新同步或副本重新启动,则将丢弃对副本的写入,但不能保证它们会自动同步。

在版本 4.0 之前,可写副本无法使设置了生存时间的键过期。 这意味着,如果您使用EXPIRE或其他为密钥设置最大 TTL 的命令时,该密钥将泄漏,虽然您在使用读取命令访问它时可能不再看到它,但您会在密钥计数中看到它,并且它仍然会使用内存。 Redis 4.0 RC3 及更高版本能够像主实例一样使用 TTL 逐出密钥,但以大于 63 的数据库编号写入的密钥除外(但默认情况下,Redis 实例只有 16 个数据库)。 请注意,即使在 4.0 以上的版本中,使用EXPIREon 一个 key 可能会导致 replica 和 master 之间的不一致。

另请注意,由于 Redis 4.0 副本写入只是本地的,不会传播到附加到实例的子副本。相反,sub-replicas 将始终接收与顶级 master 发送到中间副本的复制流相同的复制流。例如,在以下设置中:

A ---> B ---> C

便B是可写的,C 将看不到B写入,而是具有与 master 实例相同的 datasetA.

设置副本以向 master 进行身份验证

如果您的 master 通过requirepass,配置 replica 在所有同步作中使用该密码。

要在正在运行的实例上执行此作,请使用redis-cli和类型:

config set masterauth <password>

要永久设置它,请将以下内容添加到您的配置文件中:

masterauth <password>

仅允许对 N 个附加副本进行写入

从 Redis 2.8 开始,您可以将 Redis 主节点配置为 仅当当前至少有 N 个副本连接到 主人。

但是,由于 Redis 使用异步复制,因此无法确保 副本实际上收到了给定的写入,因此始终有一个数据窗口 损失。

以下是该功能的工作原理:

  • Redis 副本每秒 ping 一次主服务器,确认处理的复制流量。
  • Redis 主服务器将记住它上次收到来自每个副本的 ping 的时间。
  • 用户可以配置滞后时间不大于最大秒数的最小副本数。

如果至少有 N 个副本,且滞后小于 M 秒,则将接受写入。

您可以将其视为一种尽力而为的数据安全机制,其中无法确保给定写入的一致性,但至少数据丢失的时间窗口限制为给定的秒数。一般来说,绑定数据丢失比未绑定的数据丢失要好。

如果不满足条件,则 master 将以错误回复,并且不会接受写入。

此功能有两个配置参数:

  • min-replicas-to-write (最小副本数写入)<number of replicas>
  • 最小副本数最大滞后<number of seconds>

有关更多信息,请查看示例redis.conf文件 Redis 源分发。

Redis 复制如何处理键上的 expires

Redis 过期允许键具有有限的生存时间 (TTL)。这样的功能取决于 取决于实例计算时间的能力,但是 Redis 副本可以正确 使用 expires 复制密钥,即使这些密钥是使用 Lua 更改的 脚本。

要实现这样的功能,Redis 不能依赖 master 和 replica 具有同步的 clocks,因为这是一个无法解决的问题 ,并且会导致竞争条件和不同的数据集,因此 Redis 使用三种主要技术来复制过期的密钥 能够工作:

  1. 副本不会使密钥过期,而是等待 master 使密钥过期。当 master 使密钥过期(或由于 LRU 而驱逐它)时,它会合成一个DEL命令,该命令将传输到所有副本。
  2. 但是,由于 master-driven expire,有时副本可能仍具有逻辑上已过期的内存中的密钥,因为 master 无法提供DEL命令。为了解决这个问题,副本使用其逻辑时钟来报告一个键不存在,仅用于不违反数据集一致性的读取作(因为来自主服务器的新命令将到达)。这样,副本可以避免报告仍然存在的逻辑过期密钥。实际上,使用副本进行缩放的 HTML 片段缓存将避免返回已经早于所需生存时间的项目。
  3. 在 Lua 脚本执行期间,不会执行密钥过期。当 Lua 脚本运行时,从概念上讲,主控中的时间是冻结的,因此给定的密钥在脚本运行的所有时间内要么存在,要么不存在。这可以防止键在脚本中间过期,并且需要以保证在数据集中具有相同效果的方式将相同的脚本发送到副本。

一旦副本被提升为主节点,它将开始独立地使密钥过期,并且不需要其旧主节点的任何帮助。

在 Docker 和 NAT 中配置复制

当使用 Docker 或其他使用端口转发或网络地址转换的容器时,Redis 复制需要格外小心,尤其是在使用 Redis Sentinel 或其他主服务器INFOROLE命令输出以发现副本的地址。

问题是ROLE命令和 replication 的 这INFOoutput 在发布到 master 实例时,将显示副本 具有他们用来连接到主服务器的 IP 地址,而 使用 NAT 的环境可能与 replica instance(客户端应该用来连接到副本的那个)。

同样,副本将在配置侦听端口的情况下列出 到redis.conf,这可能与转发的端口不同,以防 端口将重新映射。

要解决这两个问题,从 Redis 3.2.2 开始,可以强制 一个副本,用于向主服务器通告任意一对 IP 和端口。 要使用的两个 configurations 指令是:

replica-announce-ip 5.5.5.5
replica-announce-port 1234

和 记录在示例中redis.conf最近的 Redis 发行版。

INFO 和 ROLE 命令

有两个 Redis 命令提供了有关当前 主实例和副本实例的复制参数。一是INFO.如果 命令使用replication参数设置为INFO replication只 将显示与复制相关的信息。另一个 more computer-friendly 命令是ROLE,该 API 的复制状态为 master 和 replica 及其复制偏移量,已连接的 副本等。

重启和故障转移后的部分同步

从 Redis 4.0 开始,当实例在故障转移后提升为 master 实例时, 它仍然能够执行与副本的部分重新同步 老主人。为此,副本会记住旧的复制 ID 和 offset 的 offset,因此可以将部分 backlog 提供给 connecting 副本,即使它们要求提供旧的复制 ID 也是如此。

但是,已提升副本的新复制 ID 将有所不同,因为它 构成数据集的不同历史记录。例如,master 可以 return available 并且可以在一段时间内继续接受写入,因此使用 提升副本中的相同复制 ID 将违反以下规则: 复制 ID 和偏移量对仅标识单个数据集。

此外,副本 - 当轻轻关闭并重新启动时 - 能够存储 在RDB提交所需的信息,以便重新同步 主人。这在升级时非常有用。当需要时,最好 使用SHUTDOWN命令以执行save & quit作 复制品。

无法部分同步通过 AOF 文件。但是,在关闭之前,实例可能会转换为 RDB 持久性 向下,然后可以重新启动,最后可以再次启用 AOF。

Maxmemory在副本上

默认情况下,副本将忽略maxmemory(除非它在故障转移后或手动提升为主服务器)。 这意味着密钥的驱逐将由 master 处理,将 DEL 命令作为 master 端的 keys 驱逐发送到副本。

此行为可确保主副本和副本保持一致,这通常是您想要的。 但是,如果副本是可写的,或者您希望副本具有不同的内存设置,并且您确定对副本执行的所有写入都是幂等的,则可以更改此默认值(但请务必了解您正在执行的作)。

请注意,由于副本默认不驱逐,因此它最终使用的内存可能会比通过maxmemory(因为副本上的某些缓冲区可能更大,或者数据结构有时可能会占用更多内存等)。 确保监控副本,并确保它们有足够的内存,以便在主节点命中配置的maxmemory设置。

要更改此行为,您可以允许副本不忽略maxmemory.要使用的配置指令是:

replica-ignore-maxmemory no
为本页评分
返回顶部 ↑