Redis:集群教程
关于
参考: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几乎同时向客户机发送回复和复制到副本的命令,因此丢失数据的窗口很小。但是,很难触发的事实并不意味着不可能触发,因此这不会改变 Redis cluster 提供的一致性保证。
这与大多数配置为“每秒将数据刷新到磁盘”的数据库发生的情况非常相似。 您可以通过强制数据库在回复客户端之前将数据刷新到磁盘来提高一致性,但这通常会导致效率过低。在 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 的一个非常重要的配置指令,称为节点超时。
- 节点超时后,主节点被视为发生故障,并且可以由其副本之一替换。类似地,在节点超时后,主节点无法感知大多数其他主节点,它会进入错误状态并停止接受写入。
Redis集群配置参数
Redis Cluster 在 redis.conf 文件中引入的配置参数:
- cluster-enabled <yes/no>:如果 yes,则在特定 Redis 实例中启用 Redis Cluster 支持。否则,该实例像往常一样作为独立实例启动。
- cluster-config-file <filename>:注意,尽管这个选项的名称,这不是一个用户可编辑的配置文件,而是一个 Redis Cluster 节点在每次有变化时自动持久化集群配置(状态,基本上)的文件,以便能够在启动时重新读取它。
- 该文件列出了集群中的其他节点、它们的状态、持久变量等内容。
- 由于某些消息接收,此文件通常会被重写并刷新到磁盘上。
- cluster-node-timeout <milliseconds>:节点超时值,Redis 集群节点不可用而不被视为失败的最长时间。
- 如果主节点在指定的时间内无法访问,它将由其副本进行故障转移。
- 该参数控制 Redis Cluster 中的其他重要事项。
- 值得注意的是,在指定的时间内无法访问大多数主节点的每个节点都将停止接受查询。
- cluster-slave-validity-factor <factor>:
- 如果设置为 0,副本将始终认为自己有效,因此无论主服务器和副本之间的链接保持断开连接的时间长短,将始终尝试故障转移主服务器。
- 如果值为正,则将节点超时值乘以此选项提供的因子,作为最大断开时间,并且如果节点是副本,在主链接断开的时间超过指定的时间,它不会尝试启动故障转移。
- 例如,如果节点超时设置为 5 秒且有效性因子设置为 10,则与主服务器断开连接超过 50 秒的副本将不会尝试对其主服务器进行故障转移。
- 请注意,如果没有用于故障转移的副本,则任何非零值都可能导致 Redis 集群在主节点故障后不可用。在这种情况下,只有当原始主节点重新加入集群时,集群才会恢复可用。
- cluster-migration-barrier <count>:一个 master 将保持连接的最小副本数,以便另一个副本迁移到不再被任何副本覆盖的 master。【?】
- cluster-require-full-coverage <yes/no>:
- 如果将其设置为 yes(默认),如果任何节点都没有覆盖一定百分比的 key space,则集群将停止接受写入。
- 如果该选项设置为 no,即使只能处理关于 key 子集的请求,集群仍将提供查询服务。
- cluster-allow-reads-when-down <yes/no>:
- 如果设置为 no(默认),当集群被标记为失败时,Redis 集群中的节点将停止服务所有流量,无论是当节点数量无法达到主机的 quorum配置,还是没有达到全覆盖。
- 这可以防止从不知道集群中的变化的节点读取可能不一致的数据。
- 可以将此选项设置为 yes 以允许在失败状态期间从节点读取,这对于希望优先考虑读取可用性但仍希望防止不一致写入的应用程序非常有用。
- 它也可以用于使用只有一个或两个分片的 Redis Cluster 时,因为它允许节点在主节点发生故障但无法自动故障转移时继续提供写入服务。
- 如果设置为 no(默认),当集群被标记为失败时,Redis 集群中的节点将停止服务所有流量,无论是当节点数量无法达到主机的 quorum配置,还是没有达到全覆盖。
创建并使用Redis集群
要创建集群,我们需要做的第一件事是在集群模式下运行几个空的 Redis 实例。这基本上意味着集群不是使用普通 Redis 实例创建的,因为需要配置特殊模式,以便 Redis 实例启用特定于集群的功能和命令。
以下是最小的 Redis 群集配置文件:
port 7000 cluster-enabled yes cluster-config-file nodes.conf cluster-node-timeout 5000 appendonly yes
如上:
- 启用集群模式需要配置 cluster enabled。
- 每个实例还包含存储此节点配置的文件的路径,默认情况下为 nodes.conf。这不是一个用户可编辑的配置文件,它由 Redis 集群实例在启动时生成,并在每次需要时更新。
请注意,按预期工作的最小集群需要包含至少三个主节点。
示例:(创建六个节点的集群)
- 请输入一个新目录,并创建以下目录,这些目录以我们将在任何给定目录中运行的实例的端口号命名。如下:
mkdir cluster-test cd cluster-test mkdir 7000 7001 7002 7003 7004 7005
- 在从 7000 到 7005 的每个目录中创建一个 redis.conf 文件。作为配置文件的模板,只需使用上面的小示例,但请确保根据目录名称配置正确的端口号。
- 现在,将从 GitHub 不稳定分支中的最新源代码编译的 redis server 可执行文件复制到集群测试目录中,最后在您喜爱的终端应用程序中打开6个终端选项卡。
- 在每个选项卡像这样启动每个实例:
cd 7000 ../redis-server ./redis.conf
- 从每个实例的日志中可以看到,由于不存在 nodes.conf 文件,每个节点都为自己分配一个新 ID:
[82462] 26 Nov 11:56:55.329 * No cluster configuration found, I'm 97a3a64667477371c4479320d683e4c8db5858b1
- 此特定实例将永远使用此 ID,以便实例在集群上下文中具有唯一的名称。每个节点都会记住使用此 ID 的每个其他节点,而不是通过IP或端口。IP地址和端口可能会更改,但在节点的整个生命周期内,唯一节点标识符永远不会更改。我们称这个标识符为节点 ID。
创建集群
现在已经有许多实例在运行,我们需要通过向节点写入一些有意义的配置来创建集群。 如果您使用的是 Redis 5 或更高版本,这是非常容易实现的,因为我们在 redis-cli 中嵌入的 Redis Cluster 命令行实用程序的帮助下,可以使用该实用程序创建新群集、检查或重新硬处理现有群集等。 对于 Redis 3 或 4 版本,有一个名为 redis-trib.rb 的旧工具,它非常类似。您可以在 Redis 源代码发行版的 src 目录中找到它。您需要安装 redis gem 才能运行 redis-trib。 “gem install redis” 如果您愿意,可以对 Redis 4 群集使用 Redis 5 的 redis-cli,而不会出现问题。
- 使用 redis-cli 为 Redis 5 创建群集,只需键入:
redis-cli --cluster create 127.0.0.1:7000 127.0.0.1:7001 \ 127.0.0.1:7002 127.0.0.1:7003 127.0.0.1:7004 127.0.0.1:7005 \ --cluster-replicas 1
- 这里使用的命令是“create”,因为我们想要创建一个新集群。选项“--cluster replicas 1”意味着我们需要为创建的每个主服务器创建一个副本。其他参数是我要用于创建新集群的实例的地址列表。
- 显然,符合我们要求的唯一设置是创建一个包含3个主机和3个副本的集群。
- redis-cli 将向您建议一种配置。通过键入 yes 接受建议的配置。集群将被配置和加入,这意味着实例将被引导到相互对话中。最后,如果一切顺利,您将看到这样一条消息:
[OK] All 16384 slots covered
- 这意味着至少有一个主实例为16384个可用插槽中的每一个提供服务。
使用 create-cluster 脚本创建Redis群集
如果您不想通过如上所述手动配置和执行单个实例来创建 Redis 集群,那么有一个简单得多的系统(但您不会学到相同数量的操作细节)。 有关如何运行脚本的详细信息,请阅读此目录中的自述文件。
只需检查 Redis 发行版中的 utils/create-cluster 目录。里面有一个名为 create-cluster 的脚本(与包含它的目录同名),它是一个简单的 bash 脚本。要启动包含3个主机和3个副本的6节点群集,只需键入以下命令:
create-cluster start
create-cluster create
当 redis-cli 实用程序希望您接受群集布局时,在步骤2中回答“是”。
您现在可以与集群交互,默认情况下,第一个节点将从端口 30001 开始。完成后,使用以下命令停止群集:
create-cluster stop
使用集群
现阶段 Redis 集群的一个问题是缺少客户端库实现。
以下是一些我知道的实现:
- redis-rb-cluster:是我(@antrez)编写的一个 Ruby 实现,作为其他语言的参考。它是一个围绕原始 redis-rb 的简单包装器,实现了与集群高效对话的最小语义。
- redis-py-cluster:redis-rb-cluster 到 Python 的端口。支持大多数 redis-py 功能。正在积极发展中。
- 流行的 Predis 支持 Redis 群集,该支持最近更新,正在积极开发中。
- 最常用的 Java 客户机 Jedis 最近增加了对 Redis Cluster 的支持。
- 请参阅项目自述中的Jedis Cluster部分。
- StackExchange.Redis 提供了对 C# 的支持(并且可以与大多数.NET语言、VB、F#等配合使用)。
- thunk-redis 提供对 Node.js 和 io.js 的支持,它是一个基于 thunk/promise 的 redis 客户端,具有流水线和集群功能。
- redis-go-cluster 是用于 Go 语言的 Redis Cluster 的实现,使用 Redigo 库客户端作为基本客户端。通过结果聚合实现 MGET/MSET。
- ioredis 是一个流行的 Node.js 客户端,为 Redis 群集提供了强大的支持。
- 当使用 -c 开关启动时,redis-cli 实用程序实现基本群集支持。
测试 Redis 群集的一个简单方法是尝试上述任何一个客户端,或者简单地使用 redis-cli 命令行实用程序。
- 以下是使用后者进行交互的示例:
$ redis-cli -c -p 7000 redis 127.0.0.1:7000> set foo bar -> Redirected to slot [12182] located at 127.0.0.1:7002 OK redis 127.0.0.1:7002> set hello world -> Redirected to slot [866] located at 127.0.0.1:7000 OK redis 127.0.0.1:7000> get foo -> Redirected to slot [12182] located at 127.0.0.1:7002 "bar" redis 127.0.0.1:7002> get hello -> Redirected to slot [866] located at 127.0.0.1:7000 "world"
redis-cli 只有非常基本的集群支持,因此它始终使用这样一个事实,即 redis 群集节点能够将客户端重定向到正确的节点。 一个严肃的客户端可以做得更好,缓存哈希槽和节点地址之间的映射,直接使用到正确节点的正确连接。仅当群集配置中发生更改时才会刷新映射(例如,在故障切换后或系统管理员通过添加或删除节点更改群集布局后)。
重新分片(Resharding)
Resharding 基本上意味着将哈希槽从一组节点移动到另一组节点,与创建集群一样,它是使用 redis-cli 实用程序完成的。
- 要开始重新分片,只需键入:
redis-cli --cluster reshard 127.0.0.1:7000
- 您只需指定集群中的一个节点,redis-cli 将自动查找其他节点。
- 目前,redis-cli 只能在管理员支持下重新硬存储,您不能只说将 5% 的插槽从该节点移动到另一个节点(但实现起来非常简单)。所以它以问题开始。
- 第一个问题是你想做多少大的重做:
How many slots do you want to move (from 1 to 16384)?
- 我们可以尝试重新硬处理1000个散列槽;
- 然后 redis-cli 需要知道重新存储的目标是什么,即接收哈希槽的节点。
- 我将使用第一个主节点,即127.0.0.1:7000(节点ID:97a3a64667477371c4479320d683e4c8db5858b1);
- 需要指定实例的节点 ID。redis-cli已将其打印在列表中,但如果需要,始终可以使用以下命令找到节点的 ID:
$ redis-cli -p 7000 cluster nodes | grep myself 97a3a64667477371c4479320d683e4c8db5858b1 :0 myself,master - 0 0 0 connected 0-5460
- 现在,将询问您想要从哪些节点获取这些密钥。
- 键入 all 以便从所有其他主节点获取一点哈希槽。
- 最终确认后,您将看到 redis-cli 将从一个节点移动到另一个节点的每个插槽的消息,并且将为从一侧移动到另一侧的每个实际键打印一个点。
- 在重新分片结束时,可以使用以下命令测试集群的运行状况:
redis-cli --cluster check 127.0.0.1:7000
- 所有的插槽都将像往常一样被覆盖,但这次 127.0.0.1:7000 的主插槽将有更多的哈希插槽,大约 6461 个。
为重新分片操作编写脚本
可以自动执行 Resharding,而无需以交互方式手动输入参数。
这可以使用以下命令行:
redis-cli --cluster reshard <host>:<port> --cluster-from <node-id> --cluster-to <node-id> --cluster-slots <number of slots> --cluster-yes
- 如果您可能经常重新硬盘,这允许构建一些自动化,但是目前 redis-cli 无法自动重新平衡群集检查 keys 在群集节点上的分布并根据需要智能移动插槽。此功能将在将来添加。
- “--cluster-yes”选项指示集群管理器对命令的提示自动回答“yes”,从而允许它在非交互模式下运行。请注意,还可以通过设置 REDISCLI_CLUSTER_YES 环境变量来激活此选项。
测试故障转移
为了触发故障转移,我们可以做的最简单的事情(这也是分布式系统中可能发生的语义上最简单的故障)是使单个进程崩溃,在我们的例子中是使单个主进程崩溃。
- 我们可以通过以下命令识别 maser 并使其崩溃:
$ redis-cli -p 7000 cluster nodes | grep master 3e3a6cb0d9a9a87168e266b0a0b24026c0aae3f0 127.0.0.1:7001 master - 0 1385482984082 0 connected 5960-10921 2938205e12de373867bf38f1ca29d31d0ddb3e46 127.0.0.1:7002 master - 0 1385482983582 0 connected 11423-16383 97a3a64667477371c4479320d683e4c8db5858b1 :0 myself,master - 0 0 0 connected 0-5959 10922-11422
- 如上 7000、7001 和 7002 是 maser。让我们使用 DEBUG SEGFAULT 命令使节点 7002 崩溃:
$ redis-cli -p 7002 debug segfault Error: Server closed the connection
- (重新启动了崩溃的实例,以便它作为副本重新加入群集)
- 现在,我们可以检查故障转移后的群集设置:
$ redis-cli -p 7000 cluster nodes 3fc783611028b1707fd65345e763befb36454d73 127.0.0.1:7004 slave 3e3a6cb0d9a9a87168e266b0a0b24026c0aae3f0 0 1385503418521 0 connected a211e242fc6b22a9427fed61285e85892fa04e08 127.0.0.1:7003 slave 97a3a64667477371c4479320d683e4c8db5858b1 0 1385503419023 0 connected 97a3a64667477371c4479320d683e4c8db5858b1 :0 myself,master - 0 0 0 connected 0-5959 10922-11422 3c3a0c74aae0b56170ccb03a76b60cfe7dc1912e 127.0.0.1:7005 master - 0 1385503419023 3 connected 11423-16383 3e3a6cb0d9a9a87168e266b0a0b24026c0aae3f0 127.0.0.1:7001 master - 0 1385503417005 0 connected 5960-10921 2938205e12de373867bf38f1ca29d31d0ddb3e46 127.0.0.1:7002 slave 3c3a0c74aae0b56170ccb03a76b60cfe7dc1912e 0 1385503418016 3 connected
- 现在 maser 在端口 7000,7001 和 7005 上运行。在端口7002上运行的 redis 实例以前是 maser,现在是 7005 的副本。
CLUSTER NODES命令的输出可能看起来很吓人,但实际上相当简单,由以下标记组成:
- Node ID:节点 ID。
- ip:port:节点的 IP 地址和端口号。
- flags:节点的角色及状态(master, replica, myself, fail, ...)。
- 如果节点是一个从节点的话, 那么跟在 flags 之后的将是主节点的节点 ID。
- 集群最近一次向节点发送 PING 命令之后,过去了多长时间还没接到回复。
- 节点最近一次返回 PONG 回复的时间。
- 节点的配置纪元(configuration epoch)。
- 本节点的网络连接情况:例如 connected 。
- 节点目前包含的槽:例如 127.0.0.1:7001 目前包含号码为 5960 至 10921 的哈希槽。
手动故障转移
有时,强制故障切换而不在主机上造成任何问题是很有用的。 例如,为了升级其中一个主节点的Redis进程,最好对其进行故障切换,以便将其转换为对可用性影响最小的副本。
Redis Cluster 支持使用 Cluster FAILOVER 命令手动故障切换,该命令必须在要进行故障切换的主机的一个副本中执行。
- 手动故障切换非常特殊,与实际主机故障导致的故障切换相比更安全,因为手动故障切换的发生方式可以避免过程中的数据丢失,仅当系统确定新主机处理了旧主机的所有复制流时,才将客户机从原始主机切换到新主机。
执行手动故障转移时,这就是您在副本日志中所看到的:
# Manual failover user request accepted. # Received replication offset for paused master manual failover: 347540 # All master replication stream processed, manual failover can start. # Start of election delayed for 0 milliseconds (rank #0, offset 347540). # Starting a failover election for epoch 7545. # Failover election won: I'm the new master.
- 基本上,连接到我们正在故障转移的主服务器的客户端被停止。同时,主机将其复制偏移量发送到复制副本,复制副本等待到达其一侧的偏移量。当达到复制偏移量时,故障切换将启动,并通知旧主机配置开关。当客户机在旧主机上解锁时,它们将重定向到新主机。
注意: 要将副本升级到 master,必须首先由群集中的大多数master知晓其为副本。否则,它就无法赢得选举。 如果复制副本刚刚添加到集群(请参阅下面的“将新节点添加为复制副本”),则在发送 CLUSTER FAILOVER 命令之前,您可能需要等待一段时间,以确保集群中的主机知道这个新的副本。
添加新节点
添加新节点基本上是添加一个空节点然后将一些数据移动到其中的过程,如果它是新的主节点,或者告诉它设置为已知节点的副本,以防它是副本。 在这两种情况下,执行的第一步都是添加一个空节点。
添加一个新节点作为master
这非常简单,只需在端口 7006 中启动一个新节点(我们已经在现有 6 个节点中使用了 7000 到 7005),除了端口号之外,其他节点都使用相同的配置,因此您应该做些什么,以符合我们在以前的节点中使用的设置:
- 创建一个新节点:
- 在终端应用程序中创建一个新选项卡。
- 进入 cluster-test 目录。
- 创建一个名为 7006 的目录。
- 在里面创建一个 redis.conf 文件,类似于用于其他节点的文件,但使用 7006 作为端口号。
- 最后启动服务器:“../redis-server ./redis.conf”
- 现在我们可以像往常一样使用 redis-cli 将节点添加到现有集群中:
redis-cli --cluster add-node 127.0.0.1:7006 127.0.0.1:7000
- 使用“add-node”命令将新节点的地址指定为第一个参数,并将集群中任意现有节点的地址指定为第二个参数。
- 实际上,redis-cli 在这里对我们帮助很小,它只是向节点发送了一个“CLUSTER MEET”消息,这也可以手动完成。然而,redis-cli 在操作之前也会检查集群的状态,因此即使您知道内部工作原理,也始终通过 redis-cli 执行集群操作是个好主意。
- 现在可以连接到新节点,看看它是否真的加入了集群:
redis 127.0.0.1:7006> cluster nodes 3e3a6cb0d9a9a87168e266b0a0b24026c0aae3f0 127.0.0.1:7001 master - 0 1385543178575 0 connected 5960-10921 3fc783611028b1707fd65345e763befb36454d73 127.0.0.1:7004 slave 3e3a6cb0d9a9a87168e266b0a0b24026c0aae3f0 0 1385543179583 0 connected f093c80dde814da99c5cf72a7dd01590792b783b :0 myself,master - 0 0 0 connected 2938205e12de373867bf38f1ca29d31d0ddb3e46 127.0.0.1:7002 slave 3c3a0c74aae0b56170ccb03a76b60cfe7dc1912e 0 1385543178072 3 connected a211e242fc6b22a9427fed61285e85892fa04e08 127.0.0.1:7003 slave 97a3a64667477371c4479320d683e4c8db5858b1 0 1385543178575 0 connected 97a3a64667477371c4479320d683e4c8db5858b1 127.0.0.1:7000 master - 0 1385543179080 0 connected 0-5959 10922-11422 3c3a0c74aae0b56170ccb03a76b60cfe7dc1912e 127.0.0.1:7005 master - 0 1385543177568 3 connected 11423-16383
请注意,由于此节点已连接到集群,因此它已经能够正确重定向客户端查询,并且通常是集群的一部分。然而,与其他大师相比,它有两个特点:
- 它没有数据,因为它没有分配的哈希槽。
- 因为它是一个没有分配 slots 的 master,所以当一个 replica 想成为 master 时,它不参与选举过程。
现在可以使用 redis-cli 的重新分片功能为该节点分配哈希槽。(像我们在上一节中所做的那样,它只是以空节点为目标的重新分片)
添加新节点作为副本
添加新副本可以通过两种方式执行:
- 使用 --cluster-slave 选项:
redis-cli --cluster add-node 127.0.0.1:7006 127.0.0.1:7000 --cluster-slave
- 请注意,这里没有指定要将复制副本添加到哪个主机。在这种情况下,redis-cli 将在副本较少的主机中随机选择某个主机,将新节点作为其副本。
- 使用 --cluster-slave 选项,及“--cluster-master-id”选项以指定要使用新副本的主服务器:
redis-cli --cluster add-node 127.0.0.1:7006 127.0.0.1:7000 --cluster-slave --cluster-master-id 3c3a0c74aae0b56170ccb03a76b60cfe7dc1912e
- 将副本添加到特定主节点的更手动方法是将新节点添加为空主节点,然后使用 CLUSTER REPLICATE 命令将其转换为副本。
- 如果节点是作为副本添加的,但您想将其作为不同主节点的副本移动,这也适用。
- 例如,为了添加节点 127.0.0.1:7005 的副本,该节点当前为 11423-16383 范围内的哈希槽提供服务,节点 ID 为 3c3a0c74aae0b56170ccb03a76b60cfe7dc1912e,需要做的就是连接新节点添加为空主,并发送命令:
redis 127.0.0.1:7006> cluster replicate 3c3a0c74aae0b56170ccb03a76b60cfe7dc1912e
- 现在我们有了这组哈希槽的新副本,集群中的所有其他节点都已经知道了(需要几秒钟来更新它们的配置)。可以使用以下命令进行验证:
$ redis-cli -p 7000 cluster nodes | grep slave | grep 3c3a0c74aae0b56170ccb03a76b60cfe7dc1912e f093c80dde814da99c5cf72a7dd01590792b783b 127.0.0.1:7006 slave 3c3a0c74aae0b56170ccb03a76b60cfe7dc1912e 0 1385543617702 3 connected 2938205e12de373867bf38f1ca29d31d0ddb3e46 127.0.0.1:7002 slave 3c3a0c74aae0b56170ccb03a76b60cfe7dc1912e 0 1385543617198 3 connected
- 节点 3c3a0c... 现在有两个副本,在端口 7002(现有的)和 7006(新的)上运行。
删除节点
要删除副本节点,只需使用 redis-cli 的“del-node”命令:
redis-cli --cluster del-node 127.0.0.1:7000 `<node-id>`
- 第一个参数只是集群中的一个随机节点,第二个参数是要删除的节点的 ID。
您也可以以相同的方式删除主节点,但是:为了删除主节点,它必须为空。如果主节点不为空,您需要在此之前将数据从它重新分片到所有其他主节点。
删除主节点的另一种方法是对其一个副本执行手动故障转移,并在节点变成新主节点的副本后删除该节点。显然,当您想减少集群中的实际主节点数量时,这无济于事,在这种情况下,需要重新分片。
副本迁移
在 Redis Cluster 中,只要使用以下命令,就可以随时重新配置一个副本对不同的主机进行复制:
redis 127.0.0.1:7006> cluster replicate <master-node-id>
但是,在一种特殊情况下,您希望在没有系统管理员的帮助下,副本从一个主机从一个主机移动到另一个主的情况。副本的自动重新配置称为副本迁移,并且能够提高 Redis 集群的可靠性。
您可能希望在特定条件下让集群副本从一个主节点移动到另一个主节点的原因是,通常 Redis 集群的抗故障能力与附加到给定主节点的副本数量一样。 例如,如果主节点和它的副本同时失败,其中每个主节点都有一个副本的集群无法继续操作,这仅仅是因为没有其他实例拥有主节点所服务的哈希槽的副本。然而,虽然网络分裂可能同时隔离多个节点,但许多其他类型的故障,如单个节点本地的硬件或软件故障,是一类非常显着的故障,不太可能同时发生时间,所以有可能在你的集群中,每个主节点都有一个副本,副本在凌晨 4 点被杀死,而主节点在早上 6 点被杀死。这仍然会导致集群无法再运行。 为了提高系统的可靠性,我们可以选择向每个主服务器添加额外的副本,但这很昂贵。副本迁移允许向少数主服务器添加更多副本。因此,您有 10 个 master,每个 master 有 1 个副本,总共有 20 个实例。但是,例如,您添加了 3 个以上的实例作为某些 master 的副本,因此某些 master 将拥有多个副本。 对于副本迁移,如果一个 master 没有副本,那么来自具有多个副本的 master 的副本将迁移到孤立的master。所以当你的副本在我们上面的例子中在凌晨 4 点宕机后,另一个副本将取代它,当主节点在凌晨 5 点也失败时,仍然可以选择一个副本,以便集群可以继续操作。
那么,简而言之,您应该了解的关于副本迁移的知识是什么?
- 集群将尝试从在给定时刻拥有最多副本的主节点迁移副本。
- 要从副本迁移中受益,您只需向集群中的单个主服务器添加更多副本,无论哪个主服务器都无关紧要。
- 有一个配置参数控制副本迁移功能,称为cluster-migration-barrier:您可以在 Redis Cluster 提供的示例 redis.conf 文件中阅读有关它的更多信息。
升级群集中的节点
升级副本节点很容易,因为您只需要停止节点并使用更新版本的Redis重新启动它。如果存在使用副本节点进行扩展读取的客户端,当给定副本不可用,它们应该能够重新连接到其他副本。
升级masters有点复杂,建议的程序是:
- 使用 CLUSTER FAILOVER 触发主服务器到其副本之一的手动故障转移。
- 等待 master 变成副本。
- 最后像复制副本一样升级节点。
- 如果您希望 master 成为您刚刚升级的节点,请触发新的手动故障转移,以便将升级后的节点恢复为 master。
迁移到Redis集群
愿意迁移到 Redis 群集的用户可能只有一个主节点,或者可能已经在使用预先存在的分片设置,其中 keys 在 N 个节点之间分割,使用一些内部算法或由其客户端库或 Redis 代理实现的分片算法。
在这两种情况下,都可以轻松迁移到 Redis Cluster,但最重要的细节是应用程序是否使用多个键操作,以及如何使用。
有三种不同的情况:
- 不使用多键操作,或事务,或涉及多键的 Lua 脚本。键是独立访问的(即使通过事务或 Lua 脚本将多个命令分组,大约相同的键,一起访问)。
- 使用多键操作、事务或涉及多个键的 Lua 脚本,但仅使用具有相同哈希标签的键,这意味着一起使用的键都有一个“{...}”恰好相同的子字符串。
- 例如,在同一个哈希标签的上下文中定义了以下多键操作:“SUNION {user:1000}.foo {user:1000}.bar”。
- 涉及多个键的多键操作、事务或 Lua 脚本,与没有显式或相同哈希标签的键名一起使用:
- 第三情况不是由 Redis Cluster 处理的:需要修改应用程序,以便不使用多键操作或仅在同一哈希标记的上下文中使用它们。
- 案例1 和案例2 已经涵盖,因此我们将重点关注这两个案例,它们以相同的方式处理,因此在文档中不会进行区分。
假设您将预先存在的数据集拆分为 N 个 master,其中 N=1 如果您没有预先存在的分片,则需要执行以下步骤才能将您的数据集迁移到 Redis Cluster:
- 停止你的客户段。目前无法自动实时迁移到Redis群集。您可以在应用程序/环境的上下文中协调实时迁移。
- 使用 BGREWRITEAOF 命令为所有 N 个 master 生成一个 append only 文件,并等待 AOF 文件完全生成。
- 将 AOF 文件从 aof-1 保存到 aof-N 某处。此时,您可以根据需要停止旧实例(这很有用,因为在非虚拟化部署中您经常需要重复使用相同的计算机)。
- 创建一个由 N 个 master 和 0 个副本组成的 Redis Cluster。稍后将添加副本。确保您的所有节点都使用 AOF 文件进行持久化。
- 停止所有集群节点,将它们的 AOF 文件替换为您预先存在的仅附加文件,第一个节点为 aof-1,第二个节点为 aof-2,最多为 aof-N。
- 使用新的 AOF 文件重新启动 Redis 集群节点。他们会抱怨说,根据他们的配置,有些 keys 不应该存在。
- 使用 redis-cli --cluster fix 命令来修复集群,以便根据每个节点是否具有权威性的哈希槽来迁移 keys。
- 最后使用 redis-cli --cluster check 以确保您的群集正常。
- 重新启动修改为使用 Redis 集群感知客户端库的客户端。
有另一种方法可以将数据从外部实例导入到 Redis 集群,即使用 redis-cli --cluster import 命令。
- 该命令将正在运行的实例的所有键(从源实例中删除键)移动到指定的预先存在的 Redis 集群。
- 但是请注意,如果您使用 Redis 2.8 实例作为源实例,则操作可能会很慢,因为 2.8 没有实现迁移连接缓存,因此您可能希望在执行此类操作之前使用 Redis 3.x 版本重新启动源实例。