Redis集群教程

本文档是 Redis 集群的温和介绍,它不使用难以理解的分布式系统概念。它提供了有关如何设置集群、测试和操作集群的说明,而不介绍Redis 集群规范中介绍的详细信息,而只是从用户角度描述系统的行为方式。
但是,本教程尝试从最终用户的角度来看提供有关 Redis 集群的可用性和一致性特征的信息,以简单易懂的方式说明。
请注意,本教程需要 Redis 版本 3.0 或更高版本。
如果您计划运行严重的 Redis 集群部署,则更正式的规范是建议的读取,即使不是严格要求。但是,从本文档开始,使用 Redis Cluster 一段时间,然后阅读规范是一个好主意。

Redis集群 101

Redis Cluster 提供了一种运行 Redis 安装的方法,其中数据在多个 Redis 节点上自动分片
Redis Cluster 在分区期间还提供一定程度的可用性,即在某些节点发生故障或无法通信时继续操作的能力。但是,集群在发生较大故障时停止运行(例如,当大多数主机不可用时)。
因此,在实际中,Redis 集群会获得什么?

  • 自动将数据集拆分为多个节点的能力
  • 节点的子集遇到故障或无法与集群的其余部分通信时,可以继续操作的能力。

Redis集群 TCP 端口

每个 Redis 集群节点都需要打开两个 TCP 连接。用于服务客户端的正常的 Redis TCP 端口(例如 6379)加上通过向数据端口添加 10000 获得的端口,因此在示例中为 16379。
个高端口用于集群总线,即使用二进制协议的节点到节点通信通道。集群总线由节点用于故障检测、配置更新、故障转移授权等。客户端不应尝试与集群总线端口通信,但应始终使用正常的 Redis 命令端口,但请确保打开防火墙中的两个端口,否则 Redis 集群节点将无法通信。
命令端口和集群总线端口偏移量是固定的,并且始终为 10000。
请注意,Redis 集群要正常工作,每个节点都需要:

  1. 用于与客户端通信的正常客户端通信端口(通常为 6379),该端口对需要到达集群的所有客户端以及所有其他集群节点(使用客户端端口进行密钥迁移)开放。
  2. 集群总线端口(客户端端口 = 10000)必须从所有其他集群节点访问。

如果未打开两个 TCP 端口,集群将不能正常工作。
集群总线使用不同的二进制协议进行节点到节点的数据交换,它更适合使用小带宽和处理时间在节点之间交换信息。

  1. # redis端口
  2. port 6371
  3. # 关闭保护模式
  4. protected-mode no
  5. # 开启集群
  6. cluster-enabled yes
  7. # 集群节点配置
  8. cluster-config-file nodes.conf
  9. # 超时
  10. cluster-node-timeout 15000
  11. #cluster-announce-ip redis-6371
  12. #cluster-announce-ip 127.0.0.1
  13. # 集群节点IP host模式为宿主机IP
  14. cluster-announce-ip 172.18.240.152
  15. # 集群节点端口 6371 - 6376
  16. cluster-announce-port 6371
  17. # 集群节点总线端口 16371 - 16376
  18. cluster-announce-bus-port 16371
  19. # 开启 appendonly 备份模式
  20. appendonly yes
  21. # 每秒钟备份
  22. appendfsync everysec
  23. # 对aof文件进行压缩时,是否执行同步操作
  24. no-appendfsync-on-rewrite no
  25. # 当目前aof文件大小超过上一次重写时的aof文件大小的100%时会再次进行重写
  26. auto-aof-rewrite-percentage 100
  27. # 重写前AOF文件的大小最小值 默认 64mb
  28. auto-aof-rewrite-min-size 64mb
  29. #对登录权限做限制,redis每个节点的requirepass可以是独立、不同的。
  30. requirepass 123456
  31. #主要是针对master对应的slave节点设置的,在slave节点数据同步的时候用到。
  32. masterauth 123456

Redis集群和Docker

当前 Redis 集群不支持 NATted 环境,在重新映射 IP 地址或 TCP 端口的一般环境中。
Docker 使用一种称为端口映射的技术:与程序认为使用的端口相比,在 Docker 容器内运行的程序可能会使用不同的端口。这对于在同一服务器中同时使用相同的端口运行多个容器非常有用。
为了使 Docker 与 Redis 集群兼容,您需要使用 Docker的主机网络模式。有关详细信息,请查看 Docker文档中的选项。--net=host

Redis集群数据分片

Redis Cluster 不使用一致的哈希,而是一种不同形式的分片形式,其中每个键在概念上都是我们称之为哈希槽的一部分
Redis 集群中有 16384 个哈希槽,为了计算给定键的哈希槽,我们只需使用密钥 modulo 16384 的 CRC16。
Redis 集群中的每个节点负责哈希槽的子集,因此,例如,您可能有一个包含 3 个节点的集群,其中:

  • 节点 A 包含从 0 到 5500 的哈希槽。
  • 节点 B 包含从 5501 到 11000 的哈希槽。
  • 节点 C 包含从 11001 到 16383 的哈希槽。

这允许轻松地添加和删除集群中的节点。例如,如果我要添加新节点 D,则需要将一些哈希槽从节点 A、B、C 移动到 D。同样,如果我想从集群中删除节点 A,我可以将 A 提供到 B 和 C 的哈希槽。当节点 A 为空时,我可以将其从集群中完全删除。
由于将哈希槽从节点移动到另一个节点不需要停止操作,因此添加和删除节点或更改节点保留的哈希槽的百分比不需要任何停机时间。
Redis Cluster 支持多个密钥操作,只要单个命令执行(或整个事务或 Lua 脚本执行)中涉及的所有键都属于同一个哈希槽。用户可以使用一个称为哈希标记的概念来强制多个键成为同一哈希槽的一部分
哈希标记记录在 Redis 集群规范中,但要点是,如果键中的 + 括号之间有一个子字符串,则只有字符串内的内容进行哈希处理,例如,并保证它们位于同一个哈希槽中,并且可以在具有多个键作为参数的命令中一起使用。this{foo}key``another{foo}key

CLUSTER SLOTS

CLUSTER SLOTS命令返回哈希槽和Redis实例映射关系。这个命令对客户端实现集群功能非常有用,使用这个命令可以获得哈希槽与节点(由IP和端口组成)的映射关系,这样,当客户端收到(用户的)调用命令时,可以根据(这个命令)返回的信息将命令发送到正确的Redis实例.

(嵌套对象)结果数组

每一个(节点)信息:

  • 哈希槽起始编号
  • 哈希槽结束编号
  • 哈希槽对应master节点,节点使用IP/Port表示
  • master节点的第一个副本
  • 第二个副本
  • …直到所有的副本都打印出来

每个结果包含该哈希槽范围的所有存活的副本,没有存活的副本不会返回.
(每个节点信息的)第三个(行)对象一定是IP/Port形式的master节点。之后的所有IP/Port都是该哈希槽范围的Redis副本。
如果一个集群实例中的哈希槽不是连续的(例如1-400,900,1800-6000),那么哈希槽对应的master和replica副本在这些不同的哈希槽范围会出现多次。

返回值

array-reply: 描述每个哈希槽范围的包含嵌套对象的列表,嵌套对象包含 IP/Port

输出示例

127.0.0.1:7001> cluster slots
1) 1) (integer) 0
   2) (integer) 4095
   3) 1) "127.0.0.1"
      2) (integer) 7000
   4) 1) "127.0.0.1"
      2) (integer) 7004
2) 1) (integer) 12288
   2) (integer) 16383
   3) 1) "127.0.0.1"
      2) (integer) 7003
   4) 1) "127.0.0.1"
      2) (integer) 7007
3) 1) (integer) 4096
   2) (integer) 8191
   3) 1) "127.0.0.1"
      2) (integer) 7001
   4) 1) "127.0.0.1"
      2) (integer) 7005
4) 1) (integer) 8192
   2) (integer) 12287
   3) 1) "127.0.0.1"
      2) (integer) 7002
   4) 1) "127.0.0.1"
      2) (integer) 7006

image.png

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 集群将无法继续运行。

CLUSTER NODES

集群中的每个节点都有当前集群配置的一个视图(快照),视图的信息由该节点所有已知节点提供,包括与每个节点的连接状态,每个节点的标记位(flags),属性和已经分配的哈希槽等等。
CLUSTER NODES提供了当前连接节点所属集群的配置信息,信息格式和Redis集群在磁盘上存储使用的序列化格式完全一样(在磁盘存储信息的结尾还存储了一些额外信息).
通常,如果你想知道哈希槽与节点的关联关系,你应该使用CLUSTER SLOTS 命令。CLUSTER NODES主要是用于管理任务,调试和配置监控。redis-trib也会使用该命令管理集群.

序列化格式

(该命令的)输出是空格分割的CSV字符串,每行代表集群中的一个节点。下面是一个示例:

07c37dfeb235213a872192d90877d0cd55635b91 127.0.0.1:30004 slave e7d1eecce10fd6bb5eb35b9f99a514335d9ba9ca 0 1426238317239 4 connected
67ed2db8d677e59ec4a4cefb06858cf2a1a89fa1 127.0.0.1:30002 master - 0 1426238316232 2 connected 5461-10922
292f8b365bb7edb5e285caf0b7e6ddc7265d2f4f 127.0.0.1:30003 master - 0 1426238318243 3 connected 10923-16383
6ec23923021cf3ffec47632106199cb7f496ce01 127.0.0.1:30005 slave 67ed2db8d677e59ec4a4cefb06858cf2a1a89fa1 0 1426238316232 5 connected
824fe116063bc5fcf9f4ffd895bc17aee7731ac3 127.0.0.1:30006 slave 292f8b365bb7edb5e285caf0b7e6ddc7265d2f4f 0 1426238317741 6 connected
e7d1eecce10fd6bb5eb35b9f99a514335d9ba9ca 127.0.0.1:30001 myself,master - 0 0 1 connected 0-5460

image.png
每行的组成结构如下:

<id> <ip:port> <flags> <master> <ping-sent> <pong-recv> <config-epoch> <link-state> <slot> <slot> ... <slot>

每项的含义如下:

  1. id: 节点ID,是一个40字节的随机字符串,这个值在节点启动的时候创建,并且永远不会改变(除非使用CLUSTER RESET HARD命令)。
  2. ip:port: 客户端与节点通信使用的地址.
  3. flags: 逗号分割的标记位,可能的值有: myself, master, slave, fail?, fail, handshake, noaddr, noflags. 下一部分将详细介绍这些标记.
  4. master: 如果节点是slave,并且已知master节点,则这里列出master节点ID,否则的话这里列出”-“。
  5. ping-sent: 最近一次发送ping的时间,这个时间是一个unix毫秒时间戳,0代表没有发送过.
  6. pong-recv: 最近一次收到pong的时间,使用unix时间戳表示.
  7. config-epoch: 节点的epoch值(or of the current master if the node is a slave)。每当节点发生失败切换时,都会创建一个新的,独特的,递增的epoch。如果多个节点竞争同一个哈希槽时,epoch值更高的节点会抢夺到。
  8. link-state: node-to-node集群总线使用的链接的状态,我们使用这个链接与集群中其他节点进行通信.值可以是 connecteddisconnected.
  9. slot: 哈希槽值或者一个哈希槽范围. 从第9个参数开始,后面最多可能有16384个 数(limit never reached)。代表当前节点可以提供服务的所有哈希槽值。如果只是一个值,那就是只有一个槽会被使用。如果是一个范围,这个值表示为起始槽-结束槽,节点将处理包括起始槽和结束槽在内的所有哈希槽。

各flags的含义 (上面所说数据项3):

  • myself: 当前连接的节点.
  • master: 节点是master.
  • slave: 节点是slave.
  • fail?: 节点处于PFAIL 状态。 当前节点无法联系,但逻辑上是可达的 (非 FAIL 状态).
  • fail: 节点处于FAIL 状态. 大部分节点都无法与其取得联系将会将改节点由 PFAIL 状态升级至FAIL状态。
  • handshake: 还未取得信任的节点,当前正在与其进行握手.
  • noaddr: 没有地址的节点(No address known for this node).
  • noflags: 连个标记都没有(No flags at all).

    发布的config epochs的说明

    slave节点在广播时,总是使用其所属master节点的config epochs值(主要是让master节点判断一下,其所持有的数据是否已经过期),所以如果你想知道slave节点本身真实的config epochs值(虽然没有什么意义,因为slave节点本身不处理哈希槽),你必须直连到该slave节点,然后执行CLUSTER NODE命令。除了直连的节点之前,命令打印出的epochs值仅仅是其他节点在心跳包中发出的值,这个值是其所属master节点的config epochs值。

    特殊的哈希槽条目格式

    通常情况下每个节点分配的哈希槽形式如下:
  1. 单哈希槽,如: 3894
  2. 范围,如: 3900-4000

但是还有两个特殊状态,是在节点被重启后,需要与其他节点确认错误的信息时(从AOF/RDB文件恢复时,发现keys的分布与节点哈希槽配置不匹配),或者当前节点正在重新分片时的需要的状态,分别是迁入(importing)迁出(migrating)
这两个状态的含义在Redis集群规范中也有解释,下面再详细介绍一下:

  • Importing 槽位还没有被分配给该节点,但是正在被迁入至当前节点。只有使用ASK命令时,才能查询这些正在迁入的槽位。
  • Migrating 槽位属于当前节点,但是正在被迁移至其他节点。只有当请求的所有key都在当前节点时,请求才会被正确响应,否则的话将使用ASK redirection强制客户端重定向至迁入节点。

状态为importing和migrating的节点在收到CLUSTER NODES命令后,输出如下:

  • 迁入节点输出: [slot_number-<-importing_from_node_id]
  • 迁出节点输出: [slot_number->-migrating_to_node_id]

下面是一个简单示例:

  • [93-<-292f8b365bb7edb5e285caf0b7e6ddc7265d2f4f]
  • [1002-<-67ed2db8d677e59ec4a4cefb06858cf2a1a89fa1]
  • [77->-e7d1eecce10fd6bb5eb35b9f99a514335d9ba9ca]
  • [16311->-292f8b365bb7edb5e285caf0b7e6ddc7265d2f4f]

请注意,输出的字符串不包含任何空格,所以CLUSTER NODES的输出仅仅是一个空格分割的CSV,即便是特殊状态的节点也遵循这个规律。.
备注:

  1. 只有节点为myself的节点才会才会被迁出和迁入,相对于节点的哈希槽,这是哈希槽所属节点的本地局部变量(Migration and importing slots are only added to the node flagged as myself. This information is local to a node, for its own slots)。
  2. 如果一个节点正在迁出或者迁入哈希槽,则这些信息只会在额外信息有所反映。如果哈希槽被分配到一个节点,并且正在迁出时,哈希槽的状态跟没有发生迁移时的状态一样,不会有什么特殊提示给客户端。

Redis集群一致性保证

Redis集群无法保证强一致性。实际上,这意味着在某些情况下,Redis 集群可能会丢失系统确认给客户端的写入。
Redis 集群可能丢失写入的第一个原因,是因为它使用异步复制。这意味着在写入过程中会发生以下情况:

  • 客户端写入主 B。
  • 主 B 回复”确定”到客户端。
  • 主 B 将写入传播到其从属 B1、B2 和 B3。

如您所见,B 不会等待 B1、B2、B3 的确认,然后再回复客户端,因为这将是 Redis 的令人望而却步的延迟处罚,因此,如果您的客户端写入了某一部分,B 将确认写入,但在能够将写入发送到其从站之前崩溃,则其中一个从属服务器(未接收写入)可以提升为主服务器, 永远失去写作。
这与大多数配置为每秒钟将数据刷新到磁盘的数据库的情况非常相似,因此,由于过去使用不涉及分布式系统的传统数据库系统的经验,您已经能够对这种情况进行推理。同样,您可以通过强制数据库在答复客户端之前将数据刷新到磁盘来提高一致性,但这通常会导致性能低得令人望而却步。这相当于 Redis 集群中的同步复制。
基本上,在性能和一致性之间需要权衡。
Redis 集群在绝对需要时支持同步写入,通过WAIT命令实现。这使得丢失写入的可能性降低得多。但是,请注意,即使使用同步复制,Redis Cluster 也不实现强一致性:在更复杂的故障情况下,始终有可能选择无法接收写入的从站作为主服务器。
另一个值得注意的情况是,Redis Cluster 将丢失写入,这种情况发生在网络分区期间,其中客户端与少数实例(至少包括主实例)隔离。
以我们的 6 个节点集群为例,这些集群由 A、B、C、A1、B1、C1 组成,包含 3 个主节点和 3 个从站。还有一个客户端,我们将调用 Z1。
发生分区后,分区的一侧可能有 A、C、A1、B1、C1,另一侧有 B 和 Z1。
Z1 仍然可以写入 B,B 将接受其写入。如果分区在很短的时间内修复,集群将正常继续。但是,如果分区持续足够长的时间将 B1 提升为分区多数端的主分区,则 Z1 在同时时间发送到 B 的写入将丢失。
请注意,Z1能够发送到B 的写入量有一个最大窗口:如果分区的大多数侧已经过足够的时间来选择从属作为主节点,则少数端中的每个主节点都将停止接受写入。
此时间量是 Redis 集群非常重要的配置指令,称为节点超时
节点超时过后,主节点将被视为失败,可以替换为其副本之一。同样,在没有主节点能够感知大多数其他主节点的情况下,节点超时过后,它会进入错误状态并停止接受写入。

Redis集群配置参数

我们即将创建一个示例集群部署。在继续之前,让我们介绍 Redis Cluster 在文件中引入的配置参数。有些会很明显,有些会更清晰,因为你继续阅读。redis.conf

  • cluster-enabled <yes/no>:如果是,则在特定的 Redis 实例中启用 Redis 集群支持。否则,实例将照常作为独立实例启动。
  • cluster-config-file <filename>请注意,尽管此选项的名称,但这不是用户可编辑的配置文件,而是 Redis 集群节点在每次发生更改时自动保留集群配置(基本状态)的文件,以便能够在启动时重新读取它。该文件列出了集群中的其他节点、其状态、持久性变量等内容。通常,由于某些消息接收,此文件在磁盘上被重写和刷新。
  • cluster-node-timeout <milliseconds> Redis 集群节点的最大可用时间量,而不被视为失败。如果主节点在超过指定时间量内无法到达,则其从属节点将将其故障通过。此参数控制 Redis 集群中的其他重要事物。值得注意的是,在指定的时间范围内无法到达大多数主节点的每个节点都将停止接受查询。
  • cluster-slave-validity-factor <factor> :如果设置为零,从站将始终认为自己有效,因此将始终尝试故障转移主服务器,无论主服务器和从站之间的链路保持断开的时间。如果该值为正值,则最大断开时间计算为节点超时值乘以此选项提供的因素,如果节点是从属,则如果主链路断开连接的时间超过指定时间量,则不会尝试启动故障转移。例如,如果节点超时设置为 5 秒,有效性系数设置为 10,则与主服务器断开连接超过 50 秒的从属服务器不会尝试故障转移其主服务器。请注意,如果没有能够故障转移的主服务器,任何与零不同的值都可能导致 Redis 集群在主故障后不可用。在这种情况下,集群将返回到仅在原始主机重新加入集群时可用。
  • cluster-migration-barrier <count>:主服务器将保持连接的最小从属数,另一个从站迁移到不再由任何从属覆盖的从站。有关详细信息,请参阅本教程中有关副本迁移的适当部分。
  • cluster-require-full-coverage <yes/no>:如果设置为”是”,则如果任何节点未覆盖某些键空间,集群将停止接受写入。如果选项设置为”否”,则即使只能处理有关密钥子集的请求,集群仍将为查询服务。
  • cluster-allow-reads-when-down <yes/no>:如果设置为否,则 Redis 集群中的节点将在集群标记为失败时停止服务所有流量,当节点无法达到主仲裁或未满足完全覆盖时。这样可以防止从不知道集群中更改的节点读取可能不一致的数据。此选项可以设置为”是”,以允许在失败状态期间从节点读取,这对于希望确定读取可用性优先级但仍希望防止不一致写入的应用程序非常有用。它也可用于使用只有一个或两个分片的 Redis 集群,因为它允许节点在主服务器故障时继续提供写入服务,但无法自动故障转移。

创建和使用 Redis 集群

注意:要手动部署 Redis 集群,了解集群的某些操作方面非常重要。但是,如果要让集群启动并运行 ASAP(尽快),请跳过此部分和下一个部分,并使用创建集群脚本直接转到创建 Redis 集群
要创建集群,我们首先需要让几个空的 Redis 实例在集群模式下运行。这基本上意味着不使用普通 Redis 实例创建集群,因为需要配置特殊模式,以便 Redis 实例将启用集群特定的功能和命令。
以下是最小 Redis 集群配置文件:

port 7000
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
appendonly yes

正如您所看到的,启用集群模式的只是指令。每个实例还包含存储此节点配置的文件的路径,默认情况下为 。这个文件从来不被人类接触过;它只需在 Redis 集群实例启动时生成,并在每次需要时更新。cluster-enabled``nodes.conf
请注意,按预期方式工作的最小集群需要至少包含三个主节点。对于第一次测试,强烈建议启动一个包含三个主节点和三个从属的六个节点集群。
为此,请输入一个新目录,然后创建以下目录,这些目录以我们将在任何给定目录中运行的实例的端口号命名。
类似:

mkdir cluster-test
cd cluster-test
mkdir 7000 7001 7002 7003 7004 7005

在每个目录(从 7000 到 7005)中创建一个文件。作为配置文件的模板,只需使用上面的小示例,但请确保根据目录名称将端口号替换为正确的端口号。redis.conf``7000
现在,将从 GitHub 不稳定分支中的最新源编译到目录中的redis-Server 可执行文件,最后在您喜爱的终端应用程序中打开 6 个终端选项卡。cluster-test
像这样启动每个实例,每个选项卡一个:

cd 7000
../redis-server ./redis.conf

正如您从每个实例的日志中可以看到的,因为不存在文件,因此每个节点都会为自己分配一个新的 ID。nodes.conf

[82462] 26 Nov 11:56:55.329 * No cluster configuration found, I'm 97a3a64667477371c4479320d683e4c8db5858b1

此 ID 将永远由此特定实例使用,以便实例在集群的上下文中具有唯一的名称。每个节点都记住使用此 ID 的所有其他节点,而不是通过 IP 或端口。IP 地址和端口可能会更改,但唯一节点标识符在节点的所有生命周期中永远不会更改。我们简单地称此标识符为节点 ID

创建集群

现在,我们已经运行了许多实例,我们需要通过向节点编写一些有意义的配置来创建集群。
如果您使用 Redis 5 或更高版本,则很容易实现,因为嵌入 到 中的 Redis Cluster 命令行实用程序帮助我们,该实用程序可用于创建新集群、检查或重新分片现有集群等。redis-cli
对于 Redis 版本 3 或 4,有称为非常相似的较旧的工具。您可以在 Redis 源代码分发的目录中找到它。您需要安装 Gem 才能运行 。redis-trib.rb``src``redis``redis-trib

gem install redis

第一个示例,即集群创建,将在 Redis 5 和 Redis 3 和 4 中同时显示。但是,接下来的所有示例将仅使用 ,因为如您所看到的,语法非常相似,并且可以通过获取有关旧语法的信息来将一个命令行更改为另一个命令行。重要提示:请注意,如果您愿意,可以使用 Redis 5 对 Redis 4 集群,而不会有问题。redis-cli``redis-trib``redis-cli``redis-trib.rb help``redis-cli
要创建 Redis 5 的集群,只需键入:redis-cli

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

用于 Redis 4 或 3 类型:redis-trib.rb

./redis-trib.rb create --replicas 1 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
显然,我们要求的唯一设置是创建一个包含 3 个主服务器和 3 个从属服务器的集群。
Redis-cli 将建议您进行配置。通过键入”是”接受建议的配置。集群将被配置并加入,这意味着,实例将被引导到相互通信中。最后,如果一切顺利,你会看到这样的消息:

[OK] All 16384 slots covered

这意味着至少有一个主实例为 16384 个可用插槽提供。

使用创建集群脚本创建 Redis 集群

如果您不想通过手动配置和执行单个实例来创建 Redis 集群,如上文所述,则系统要简单得多(但您不会学到相同数量的操作详细信息)。
只需在 Redis 分发中检查目录。里面有一个脚本(与它包含的目录同名),它是一个简单的 bash 脚本。要启动包含 3 个主服务器和 3 个从属的 6 个节点集群,只需键入以下命令:utils/create-cluster``create-cluster

  1. create-cluster start
  2. create-cluster create

当实用程序希望接受集群布局时,请回复步骤 2 中的操作。yes``redis-cli
现在,您可以与集群交互,默认情况下,第一个节点将从端口 30001 开始。完成后,请用以下资源停止集群:

  1. create-cluster stop.

有关如何运行脚本,请阅读此目录内部。README

玩集群

在此阶段,Redis 集群的一个问题就是缺少客户端库实现。
我知道以下实现:

  • redis-rb-cluster是我(@antirez)编写的 Ruby 实现,作为其他语言的参考。它是围绕原始 redis-rb 的简单包装,实现最小语义以有效地与集群进行通信。
  • redis-py-cluster redis-rb-cluster 集群到 Python 的端口。支持大多数redis-py功能。正在积极开发中。
  • 流行的Predis支持 Redis 集群,该支持最近更新,并且正在积极开发中。
  • 使用最多的 Java 客户端Jedis 最近添加了对 Redis 集群的支持,请参阅项目 README 中的 Jedis集群部分。
  • StackExchange.Redis支持 C#(并且应该可以正常使用大多数 .NET 语言;VB、F#等)
  • thunk-redis支持节点.js和 io .js,它是一个基于 thunk/承诺的 redis 客户端,具有管道和集群。
  • redis-go-cluster 是使用 Redigo 库客户端作为基本客户端的Go 语言 Redis 集群的实现。通过结果聚合实现 MGET/MSET。
  • ioredis是一个流行的.js客户端,为 Redis 集群提供了强大的支持。
  • 该实用程序在交换机启动时实现基本的集群支持。redis-cli``-c

测试 Redis Cluster 的一种简单方法就是尝试上述任何客户端,或者只需尝试命令行实用程序。以下是使用后者进行交互的示例: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:7000> get hello
-> Redirected to slot [866] located at 127.0.0.1:7000
"world"

注意:如果使用脚本创建集群,则节点可能会侦听不同的端口,默认情况下从 30001 开始。
redis-cli 集群支持非常基本,因此它始终使用 Redis 集群节点能够将客户端重定向到正确的节点这一事实。严肃的客户端能够做得更好,并在哈希槽和节点地址之间缓存映射,以便直接使用正确的连接到正确的节点。仅在集群配置中发生更改时(例如故障转移后或系统管理员通过添加或删除节点更改集群布局后)刷新映射。

使用 redis-rb 集群编写示例应用

在继续演示如何操作 Redis 集群、执行故障转移或重新碎片等操作之前,我们需要创建一些示例应用程序,或者至少能够理解简单的 Redis 集群客户端交互的语义。
这样,我们可以运行一个示例,同时尝试使节点失败,或开始重新沙起,以查看 Redis 集群在真实条件下的行为方式。在没有人写入集群时,查看会发生什么,这一点不是很有帮助。
本节介绍redis-rb-集群的一些基本用法,其中显示了两个示例。第一个如下,是redis-rb集群分布中的示例.rb 文件:

require './cluster'

if ARGV.length != 2
   startup_nodes = [
       {:host => "127.0.0.1", :port => 7000},
       {:host => "127.0.0.1", :port => 7001}
   ]
else
   startup_nodes = [
      {:host => ARGV[0], :port => ARGV[1].to_i}
  ]
end

rc = RedisCluster.new(startup_nodes,32,:timeout => 0.1)

last = false

while not last
  begin
      last = rc.get("__last__")
      last = 0 if !last
  rescue => e
      puts "error #{e.to_s}"
      sleep 1
  end
end

((last.to_i+1)..1000000000).each{|x|
  begin
      rc.set("foo#{x}",x)
      puts rc.get("foo#{x}")
      rc.set("__last__",x)
  rescue => e
      puts "error #{e.to_s}"
  end
  sleep 0.1
}

应用程序做一件非常简单的事情,它把窗体中的键一个一个地设置。因此,如果运行该程序,结果如下命令流:foo<number>``number

  • 设置 foo0 0
  • 设置 foo1 1
  • 设置 foo2 2
  • 等等…

程序看起来比通常应该的要复杂得多,因为它旨在在屏幕上显示错误,而不是异常退出,因此对集群执行的每个操作都用块包装。begin``rescue
14 行是程序中的第一条有趣的行。它创建 Redis Cluster 对象,使用启动节点列表作为参数,允许此对象针对不同节点使用的最大连接数,最后将给定操作后超时视为失败。
启动节点不需要是集群的所有节点。重要的是,至少一个节点是可访问的。另请注意,redis-rb-集群在能够连接到第一个节点后,将更新此启动节点列表。您应该期望与任何其他严重客户端发生此类行为。
现在,我们已经将 Redis Cluster 对象实例存储在 rc 变量中我们就可以使用该对象,就像它是一个正常的 Redis 对象实例一样。
这正是第18 行到第 26行中发生的情况:当我们重新启动示例时,我们不想再次以 启动 ,因此我们将计数器存储在 Redis 本身中。上面的代码旨在读取此计数器,或者如果计数器不存在,则为其分配零值。foo0
但是请注意它是如何 while 循环,因为我们希望重试,即使集群已关闭并返回错误。普通应用程序不需要如此小心。
28 和 37 之间的行启动主循环,其中设置了键或显示错误。
请注意循环末尾的调用。在测试中,如果您想要尽可能快地写入集群,可以删除睡眠状态(相对而言,这是一个没有实际并行的繁忙循环,因此在最佳条件下,通常得到 10k 操作/秒)。sleep
通常,写入速度会减慢,以便示例应用程序更易于人类遵循。
启动应用程序将生成以下输出:

ruby ./example.rb
^C (I stopped the program here)

这不是一个非常有趣的程序,我们将在一个时刻使用一个更好的,但我们已经可以看到在重新碎片时,程序正在运行会发生什么。

集群重新分片

现在,我们已准备好尝试集群重新分片。为此,请保持示例.rb 程序运行,以便查看是否对正在运行的程序产生一些影响。此外,您可能需要注释调用,以便在重新分片过程中有一些更严重的写入负载。sleep
重新沙丁通常意味着将哈希槽从一组节点移动到另一组节点,就像集群创建一样,它使用 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。这已经通过 redis-cli 在列表中打印,但如果需要,我始终可以找到具有以下命令的节点的 ID:

$ redis-cli -p 7000 cluster nodes | grep myself
97a3a64667477371c4479320d683e4c8db5858b1 :0 myself,master - 0 0 0 connected 0-5460

好的, 所以我的目标节点是 97a3a646677371c4479320d683e4c8db5858b1 。
现在,您将从要获取这些密钥的节点中收到问题。我只是键入, 以便从所有其他主节点中获得一点哈希插槽。all
最终确认后,你会看到每个插槽的消息,即 redis-cli 将从一个节点移动到另一个插槽,并且将打印一个点,用于从一侧移动到另一侧的每个实际键。
在重新分片进行中,您应该能够看到示例程序运行不受影响。如果需要,可以在重新分片期间多次停止并重新启动它。
在重新分片结束时,您可以使用以下命令测试集群的运行状况:

redis-cli --cluster check 127.0.0.1:7000

所有插槽将照常覆盖,但这次主在 127.0.0.1:7000 将有更多的哈希插槽,大约 6461。

编写重新分片操作的脚本

可以自动执行重新分片,而无需以交互方式手动输入参数。这使用如下命令行是可能的:

redis-cli --cluster reshard <host>:<port> --cluster-from <node-id> --cluster-to <node-id> --cluster-slots <number of slots> --cluster-yes

如果您可能经常重新分片,则允许构建一些自动化,但是目前无法自动重新平衡集群检查集群节点之间的密钥分布,并根据需要智能移动插槽。将来将添加此功能。redis-cli

更有趣的示例应用程序

我们之前写的示例应用程序不是很好。它以简单的方式写入集群,甚至不检查所写的东西是否正确。
从我们的角度来看,接收写入的集群可以始终将密钥写入每个操作,我们完全不会注意到。foo``42
因此,在存储库中,有一个更有趣的应用程序,称为 。默认情况下,它使用一组计数器 1000,并发送INCR命令以增加计数器。redis-rb-cluster``consistency-test.rb
但是,应用程序不只是编写,而是执行两项其他工作:

  • 当使用INCR 更新计数器时,应用程序会记住写入。
  • 它还在每次写入前读取一个随机计数器,并检查该值是否为我们所期望的值,并将其与内存中的值进行比较。

这意味着,这个应用程序是一个简单的一致性检查器,并且能够告诉您,如果集群失去了一些写入,或者如果它接受了一个写入,我们没有收到确认。在第一种情况下,我们将看到计数器的值小于我们记住的值,而在第二种情况下,该值将更大。
运行一致性测试应用程序会每隔一秒产生一行输出:

$ ruby consistency-test.rb
925 R (0 err) | 925 W (0 err) |
5030 R (0 err) | 5030 W (0 err) |
9261 R (0 err) | 9261 W (0 err) |
13517 R (0 err) | 13517 W (0 err) |
17780 R (0 err) | 17780 W (0 err) |
22025 R (0 err) | 22025 W (0 err) |
25818 R (0 err) | 25818 W (0 err) |

该行显示执行的Reads和 W仪式的数量,以及错误数(由于系统不可用而未接受查询)。
如果发现某些不一致,则向输出中添加新行。例如,如果我在程序运行时手动重置计数器,则会发生这种情况:

$ redis-cli -h 127.0.0.1 -p 7000 set key_217 0
OK
(in the other tab I see...)
94774 R (0 err) | 94774 W (0 err) |
98821 R (0 err) | 98821 W (0 err) |
102886 R (0 err) | 102886 W (0 err) | 114 lost |
107046 R (0 err) | 107046 W (0 err) | 114 lost |

当我将计数器设置为 0 时,实际值为 114,因此程序报告 114个丢失的写入(集群未记住的 INCR 命令)。
作为测试用例,此程序更有趣,因此我们将使用它来测试 Redis 集群故障转移。

测试故障转移

注意:在此测试期间,应打开一个选项卡,同时运行一致性测试应用程序。
为了触发故障转移,我们能做的最简单的事情(这也是在分布式系统中可能发生的语义上最简单的故障)是崩溃单个进程,在我们的例子中是单个主机。
我们可以识别主服务器,并使用以下命令崩溃:

$ 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 是大师。让我们使用 DEBUG SEGFAULT命令崩溃节点 7002:

$ redis-cli -p 7002 debug segfault
Error: Server closed the connection

现在,我们可以查看一致性测试的输出,看看它报告了什么。

18849 R (0 err) | 18849 W (0 err) |
23151 R (0 err) | 23151 W (0 err) |
27302 R (0 err) | 27302 W (0 err) |
... many error warnings here ...
29659 R (578 err) | 29660 W (577 err) |
33749 R (578 err) | 33750 W (577 err) |
37918 R (578 err) | 37919 W (577 err) |
42077 R (578 err) | 42078 W (577 err) |

正如在故障转移期间看到的,系统无法接受 578 次读取和 577 次写入,但数据库中未创建不一致。这听起来可能出乎意料,因为在本教程的第一部分中,我们指出,Redis 集群可能会丢失在故障转移期间的写入,因为它使用异步复制。我们没有说,这是不太可能发生的,因为 Redis 发送答复到客户端,并命令复制到从属服务器,大约在同一时间,所以有一个很小的窗口丢失数据。但是,很难触发并不意味着这是不可能的,因此这不会改变 Redis 集群提供的一致性保证。
现在,我们可以检查故障转移后集群设置(请注意,在此期间,我重新启动了崩溃的实例,以便它重新加入集群作为从属):

$ 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

现在,主机在端口 7000、7001 和 7005 上运行。以前的主服务器(即端口 7002 上运行的 Redis 实例)现在是 7005 的从站。
CLUSTER NODES命令的输出可能看起来很吓人,但它实际上非常简单,并且由以下令牌组成:

  • 节点 ID
  • ip:端口
  • 旗帜: 主人, 奴隶, 我自己, 失败, … …
  • 如果是从站,则主节点 ID
  • 最后一个挂起的 PING 的时间仍在等待回复。
  • 上次接收 PONG 的时间。
  • 此节点的配置纪元(请参阅集群规范)。
  • 指向此节点的链接的状态。
  • 插槽已送达…

手动故障转移

有时,强制故障转移而不实际导致主机上出现任何问题是很有用的。例如,为了升级其中一个主节点的 Redis 进程,我们更想进行故障转移,以便将其转变为从属节点,对可用性的影响最小。
Redis 集群使用 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.

基本上,连接到我们故障的主机的客户端将停止。同时,主服务器将其复制偏移量发送到从站,该从站等待到达其侧面的偏移量。当达到复制偏移量时,将启动故障转移,并通知旧主机有关配置开关。当客户端在旧主机上解除阻止时,它们将重定向到新主服务器。

添加新节点

添加新节点基本上是添加空节点,然后将一些数据移动到其中的过程,以防它是新主节点,或告诉其设置为已知节点的副本,以防它是从节点。
我们将同时显示这两个实例,从添加新的主实例开始。
在这两种情况下,要执行的第一步是添加一个空节点
这很简单,只需在端口 7006(我们已经使用 7000 到 7005,用于现有 6 个节点)中的新节点,除了端口号之外,其他节点的配置相同,因此您应该执行哪些操作以符合我们为以前的节点使用的设置:

  • 在终端应用程序中创建新选项卡。
  • 输入目录。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

如您所见,我使用外接节点命令,将新节点的地址指定为第一个参数,将集群中的随机现有节点的地址指定为第二个参数。
实际上,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

请注意,由于此节点已连接到集群,因此它已经能够正确重定向客户端查询,并且一般说是集群的一部分。然而,与其他母版相比,它有两个特点:

  • 它不保存任何数据,因为它没有分配的哈希插槽。
  • 因为它是没有分配插槽的主服务器,所以当从站想要成为主服务器时,它不参与选举过程。

现在,可以使用 的重新分片功能将哈希槽分配给此节点。它基本上是无用的显示这一点,正如我们已经在上一节,没有区别,它只是一个重新碎片有一个目标的空节点。redis-cli

将新节点添加为副本

添加新副本可以通过两种方式执行。显而易见的是再次使用 redis-cli,但使用 —集群从属选项,像这样:

redis-cli --cluster add-node 127.0.0.1:7006 127.0.0.1:7000 --cluster-slave

请注意,此处的命令行与用于添加新主服务器的命令行完全一样,因此我们不指定要向哪个主副本添加副本。在这种情况下,redis-cli 将在具有较少副本的主副本之间将新节点添加为随机主机的副本。
但是,您可以使用以下命令行准确指定要使用新副本定位的主机:

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 REPLICATE <master-node-id>

但是,有一种特殊方案,您希望副本在没有系统管理员帮助的情况下自动从一个主副本移动到另一个主副本。副本的自动重新配置称为副本迁移,能够提高 Redis 集群的可靠性。
注意:您可以在 Redis 集群规范中阅读副本迁移的详细信息,在这里,我们将仅提供有关一般想法以及您应该执行哪些操作以从中受益的信息。
在特定条件下,您可能希望让集群副本从一个主副本移动到另一个主副本,原因是 Redis 集群通常与附加到给定主机的副本数一样具有抗故障能力。
例如,如果主副本及其副本同时发生故障,则每个主机具有单个副本的集群无法继续操作,只是因为没有其他实例具有主副本服务的哈希槽的副本。但是,尽管网络拆分可能会同时隔离多个节点,但许多其他类型的故障(如单个节点的本地硬件或软件故障)是一类非常值得注意的故障,不太可能同时发生,因此在集群中,每个主节点都有从站,因此从属服务器可能于凌晨 4 点被杀死。, 主人早上 6 点被杀这仍会导致集群无法再运行。
为了提高系统的可靠性,我们可以选择向每个主机添加额外的副本,但这非常昂贵。副本迁移允许向几个主服务器添加更多从属服务器。因此,您有 10 个主机,每个主机有 1 个从属服务器,总共 20 个实例。但是,例如,添加 3 个实例作为某些主的从属,因此某些主级将具有多个从属服务器。
在副本迁移时,发生的情况是,如果主副本没有从站,则具有多个从属服务器的主副本将迁移到孤立主服务器。因此,在您的奴隶凌晨 4 点关闭后,如我们上面的示例所示,另一个从属人将代替它,当主服务器也于凌晨 5 点发生故障时,仍有一个从属服务器可以当选,以便集群可以继续运行。
那么,您应该了解的复制副本迁移简而言之呢?

  • 集群将尝试从在给定时刻具有最大副本的主机迁移副本。
  • 若要从副本迁移中获益,只需向集群中的单个主机添加更多副本,则与什么主副本有关。
  • 有一个配置参数控制副本迁移功能,称为 :您可以在 Redis Cluster 提供的示例文件中阅读有关它的详细信息。cluster-migration-barrier``redis.conf

升级 Redis 集群中的节点

升级从属节点非常简单,因为您只需停止节点,然后使用 Redis 的更新版本重新启动它。如果有客户端使用从属节点缩放读取,则如果给定的从节点不可用,则它们应该能够重新连接到其他从属节点。
升级母版要复杂一些,建议的过程是:

  1. 使用 CLUSTER 故障转移触发主服务器到其从属服务器之一的手动故障转移(请参阅本文档的”手动故障转移”部分)。
  2. 等主节点变成从节点。
  3. 最后像升级从站一样升级节点。
  4. 如果希望主节点是刚刚升级的节点,请触发新的手动故障转移,以便将升级的节点返回为主节点。

按照此过程,您应该一个节点进行一个多个节点的升级,直到升级所有节点。

迁移到 Redis 集群

愿意迁移到 Redis Cluster 的用户可能只有一个主服务器,或者已经使用预先存在的分片设置,其中密钥在 N 个节点之间拆分,使用一些内部算法或由其客户端库或 Redis 代理实现的分片算法。
在这两种情况下,都可以很容易地迁移到 Redis 集群,但最重要的细节是,如果应用程序使用多键操作,以及如何。有三种不同的情况:

  1. 不使用多个密钥操作或事务或涉及多个密钥的 Lua 脚本。密钥是独立访问的(即使通过事务或 Lua 脚本访问,将多个命令(大约同一个键)分组在一起)。
  2. 使用多个密钥操作、事务或 Lua 脚本,涉及多个密钥,但仅使用具有相同哈希标记的密钥,这意味着一起使用的键都有一个恰好是相同的子字符串。例如,在同一哈希标记的上下文中定义以下多个键操作: 。{...}``SUNION {user:1000}.foo {user:1000}.bar
  3. 涉及多个密钥的多个密钥操作、事务或 Lua 脚本用于密钥名称,这些名称没有显式或相同的哈希标记。

第三个案例不是由 Redis Cluster 处理:应用程序需要修改,以便不使用多键操作,或只在同一哈希标记的上下文中使用它们。
案例 1 和案例 2 都包括在内,因此我们将重点介绍以相同方式处理的这两种情况,因此在文档中不会区分。
假设您将预先存在的数据集拆分为 N 个母版,其中 N+1(如果您没有预先存在的分片,则需要执行以下步骤才能将数据集迁移到 Redis 集群:

  1. 停止您的客户。当前无法自动实时迁移到 Redis 集群。您可以在应用程序/环境的上下文中协调实时迁移。
  2. 使用 BGREWRITEAOF 命令为所有 N 主机生成仅追加文件,并等待完全生成 AOF 文件。
  3. 将 AOF 文件从 aof-1 保存到 Aof-N 某处。此时,您可以停止旧实例(这非常有用,因为在非虚拟化部署中,您通常需要重用相同的计算机)。
  4. 创建由 N 个主服务器和零从站组成的 Redis 集群。稍后将添加从属服务器。确保所有节点都使用仅追加文件进行持久性。
  5. 停止所有集群节点,将仅追加文件替换为预先存在的仅追加文件,aof-1 表示第一个节点,aof-2 表示第二个节点,最多为 aof-N。
  6. 使用新的 AOF 文件重新启动 Redis 集群节点。他们会抱怨有键不应该在那里根据他们的配置。
  7. 使用命令来修复集群,以便根据每个节点是否具有权威性的哈希槽迁移密钥。redis-cli --cluster fix
  8. 在末尾使用 以确保集群正常。redis-cli --cluster check
  9. 重新启动修改以使用 Redis 集群感知客户端库的客户端。

还有一种从外部实例将数据导入 Redis 集群的替代方法,即使用 命令。redis-cli --cluster import
该命令将正在运行的实例的所有键(从源实例中删除密钥)移动到指定的预先存在的 Redis 集群。但是请注意,如果使用 Redis 2.8 实例作为源实例,则操作可能很慢,因为 2.8 不实现迁移连接缓存,因此您可能需要在执行此类操作之前使用 Redis 3.x 版本重新启动源实例。

来源

https://redis.io/topics/cluster-tutorial
http://www.redis.cn/