Redis:集群教程
Eijux(讨论 | 贡献)2021年11月4日 (四) 07:31的版本 (建立内容为“category:Redis == 关于 == 参考:[https://redis.io/topics/cluster-tutorial https://redis.io/topics/cluster-tutorial] 以下包含了有关如何设…”的新页面)
关于
参考:https://redis.io/topics/cluster-tutorial 以下包含了有关如何设置集群、测试和操作集群的说明,而不涉及Redis 集群规范中涵盖的细节。
Redis 集群
Redis Cluster 提供了一种运行 Redis 安装的方法,其中数据在多个 Redis 节点之间自动分片。 Redis Cluster 还在分区期间提供了一定程度的可用性,这实际上是在某些节点出现故障或无法通信时继续操作的能力。但是,如果发生较大的故障(例如,当大多数主节点不可用时),集群将停止运行。
那么在实践中,您从 Redis Cluster 中得到了什么?
- 在多个节点之间自动拆分数据集的能力。
- 当部分节点出现故障或无法与集群的其余部分通信时继续操作的能力。
Redis 集群:TCP 端口
每个 Redis Cluster 节点都需要打开两个 TCP 连接:
- 用于为客户端提供服务的普通 Redis TCP 端口(如:6379);
- 以及名为“集群总线端口”(Cluster Bus Port)的第二个端口。
- 集群总线端口:将数据端口+10000(如:16379)添加10000;或使用集群端口配置覆盖。
第二个高端口用于集群总线,即使用二进制协议的节点到节点通信通道。节点使用集群总线进行故障检测、配置更新、故障转移授权等。 客户端永远不要尝试与集群总线端口通信,而应始终与正常的 Redis 命令端口通信,但请确保在防火墙中打开这两个端口,否则 Redis 集群节点将无法通信。
请注意,要使 Redis 集群正常工作,您需要为每个节点:
- 用于与客户端通信的普通客户端通信端口(通常为6379)将向需要到达群集的所有客户端以及所有其他群集节点(使用客户端端口进行密钥迁移)开放。
- 集群总线端口必须可以从所有其他群集节点访问。
- 如果您不同时打开两个 TCP 端口,您的集群将无法按预期工作。
集群总线使用一种不同的二进制协议进行节点到节点的数据交换,这种协议更适合于使用较少的带宽和处理时间在节点之间交换信息。
Redis集群 和 Docker
目前,Redis Cluster 不支持 NATED 环境,也不支持 IP 地址或 TCP 端口重新映射的一般环境。 Docker 使用了一种称为端口映射的技术:运行在 Docker 容器中的程序可能会使用不同的端口公开,而该端口与程序认为正在使用的端口不同。这对于在同一服务器上同时使用相同端口运行多个容器非常有用。
为了使 Docker 与 Redis 群集兼容,您需要使用 Docker 的主机网络模式。
- 有关更多信息,请查看 Docker 文档中的“--net=host”选项。
Redis集群:数据分片
Redis Cluster 不使用[一致哈希(consistency hashing)],而是一种不同形式的分片,其中每个键在概念上都是我们所称的散列槽的一部分。 Redis 集群中有 16384(2 ^ 14)个哈希槽,使用公式 CRC16(key) % 16384 来计算键 key 属于哪个槽。
Redis 集群中的每个节点负责哈希槽的子集。
- 因此,例如,您可能有一个包含3个节点的集群,其中:
- 节点 A 包含从 0 到 5500 的哈希槽。
- 节点 B 包含从 5501 到 11000 的哈希槽。
- 节点 C 包含从 11001 到 16383 的哈希槽。
这允许在集群中轻松地添加和删除节点。
- 例如,如果我想添加一个新节点 D,我需要将一些哈希槽从节点 A、B、C 移动到 D。类似地,如果我想从集群中删除节点 A,我可以只将由 A 提供服务的哈希槽移动到 B 和 C。当节点 A 为空时,我可以将其完全从集群中删除。
- 由于将哈希槽从一个节点移动到另一个节点不需要停止操作,因此添加和删除节点,或更改节点持有的哈希槽的百分比,不需要任何停机时间。
Redis Cluster 支持多个键操作,只要单个命令执行(或整个事务,或 Lua 脚本执行)中涉及的所有键都属于同一个哈希槽。 用户可以使用名为哈希标记(hash tags)的概念强制多个键成为同一哈希槽的一部分。
哈希标记记录在 Redis 集群规范中,其要点:如果在一个键的“{}”括号之间有一个子字符串,则只对字符串括号“{}”内的内容进行哈希处理。
- 例如“this{foo}key”和“another{foo}key”保证位于同一个哈希槽中,并且可以在具有多个键作为参数的命令中一起使用。
Redis集群:主从模型
为了在主节点子集出现故障或无法与大多数节点通信时保持可用,Redis Cluster 使用主从模型,其中每个哈希槽具有1个(主节点本身)到 N 个副本(N-1 个附加副本节点)。
在具有节点 A、B、C 的示例集群中,如果节点 B 出现故障,集群将无法继续,因为我们不再能够提供 5501-11000 范围内的哈希槽。
但是,在创建集群时(或稍后),我们会向每个主节点添加一个副本节点,这样最终的集群就由:主节点 A、B、C,从节点 A1、B1、C1 组成。
这样,如果节点 B 出现故障,系统就能够继续:节点 B1 复制 B,如果 B 失败,集群将提升节点 B1 作为新的主节点,并将继续正常运行。
- 但是,请注意,如果节点 B 和 B1 同时出现故障,Redis 群集将无法继续运行。
Redis集群:一致性保证
Redis Cluster 无法保证强一致性(strong consistency)。实际上,这意味着在某些情况下,Redis Cluster 可能会丢失系统已向客户端确认的写入。
Redis Cluster 会丢失写入的第一个原因是因为它使用异步复制。
- 这意味着在写入期间会发生以下情况:
- 您的客户端写入主 B。
- 主 B 向您的客户端回复 OK。
- 主 B 将写入传播到其副本 B1、B2 和 B3。
- 如您所见,B 在回复客户端之前不会等待来自 B1、B2、B3 的确认,因为这对 Redis 来说是一个令人望而却步的延迟惩罚,因此如果您的客户端写入某些内容,B 会确认写入,但如果在将写入发送到其副本之前崩溃,其中一个副本(未收到写入)可以提升为主,那么写入的内容将永远丢失。
这与大多数配置为“每秒将数据刷新到磁盘”的数据库发生的情况非常相似 您可以通过强制数据库在回复客户端之前将数据刷新到磁盘来提高一致性,但这通常会导致效率过低。在 Redis Cluster 的情况下,这相当于同步复制。 基本上,需要在性能和一致性之间进行权衡。 Redis 集群在绝对需要时支持同步写入,通过 WAIT 命令实现。这使得丢失写入的可能性大大降低。但是,请注意,即使使用同步复制,Redis Cluster 也没有实现强一致性:在更复杂的故障场景下,始终有可能将无法接收写入的副本选为 master。
还有一个值得注意的场景是 Redis 集群将丢失写入,这发生在网络分裂期间,客户端与少数实例(至少包括一个主实例)隔离。
- 以我们的 6 个节点集群为例,它由 A、B、C、A1、B1、C1 组成,有 3 个主节点和 3 个副本节点。还有一个客户端,我们将其称为 Z1。
- 分区发生后,可能在分区的一侧有 A、C、A1、B1、C1,而在另一侧有 B 和 Z1。
- Z1 仍然能够写入 B,B 将接受其写入。如果分区在很短的时间内恢复,集群将继续正常运行。但是,如果分区持续足够的时间让 B1 在分区的多数侧提升为 master,则 Z1 同时发送给 B 的写入将丢失。
- 请注意,Z1 能够发送到 B 的写入量有一个最大窗口:如果分区的多数侧已经有足够的时间来选举一个副本作为主节点,则少数侧的每个主节点都将停止接受写入。
这个时间量是 Redis Cluster 的一个非常重要的配置指令,称为节点超时。
- 节点超时后,主节点被视为发生故障,并且可以由其副本之一替换。类似地,在节点超时后,主节点无法感知大多数其他主节点,它会进入错误状态并停止接受写入。