离线下载
PDF版 ePub版

powersoft · 更新于 2017-11-18 05:00:56

集群(下)

手动故障转移(Manual failover)

有时候在主服务器事实上没有任何故障的情况下强制一次故障转移是很有用的。例如,为了升级主服务器节点中的一个进程,可以对其进行故障转移使其变为一个从服务器,这样最小化了对可用性的影响。

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.  

基本上,连接到我们正在故障转移的主服务器的客户端停止了。与此同时,主服务器发送复制偏移量给从服务器,等待到达这个偏移量。当复制偏移量到达以后,故障转移就开始了,旧的主服务器被通知切换配置。当客户端在旧主服务器上解除阻塞时,就被重定向到新的主服务器。

添加新节点(Adding a new node)

添加一个新节点的过程基本上就是,添加一个空节点,然后,如果是作为主节点则移动一些数据进去,如果是从节点则其作为某个节点的副本。

两种情况我们都会讨论,先从添加一个新的主服务器实例开始。

两种情况下,第一步要完成的都是添加一个空节点。

我们使用与其他节点相同的配置(端口号除外)在 7006 端口(我们已存在的 6 个节点已经使用了从 7000 到 7005 的端口)上开启一个新的节点,那么为了与我们之前的节点布局一致,你得这么做:

  • 在你的终端程序中开启一个新的标签窗口。
  • 进入 cluster-test 目录。
  • 创建一个名为 7006 的目录。
  • 在里面创建一个 redis.conf 的文件,类似于其它节点使用的文件,但是使用 7006 作为端口号。
  • 最后使用../redis-server ./redis.conf 启动服务器。

此时服务器已经在运行中了。

现在我们可以像通常一样使用 redis-trib 来添加节点到已存在的集群中。

./redis-trib.rb add-node 127.0.0.1:7006 127.0.0.1:7000  

你可以看到,我使用了 addnode 命令,指定新的节点地址为第一个参数,集群中一个随机存在的节点的地址作为第二个参数。

实际上 redis-trib 在这里对我们只有很少的帮助,只是发送了一个 CLUSTER MEET 消息到节点,这些也可以手动完成。但是 redis-trib 也在操作之前检查了集群的状态,所以即便你知道内部是如何工作的,一直通过 redis-trib 来执行集群操作也是一个不错的主意。

现在,我们可以连接到这个新的节点,看看它是否真的加入到了集群中:

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-trib 的重新分片特性来给这个节点赋予哈希槽了。基本上没有必现展示这个了,因为我们已经在之前的小节中展示过了,没有什么不同,只是以空节点为目标的一次重分片。

添加副本节点(Adding a new node as a replica)

添加一个新副本可以有两种方式。显而易见的一种方式是再次使用 redis-trib,但是要使用—slave 选项,像这样:

./redis-trib.rb add-node --slave 127.0.0.1:7006 127.0.0.1:7000  

注意,这里的命令行完全像我们在添加一个新主服务器时使用的一样,所以我们没有指定要给哪个主服务器添加副本。这种情况下,redis-trib 会添加一个新节点作为一个具有较少副本的随机的主服务器的副本。

但是,你可以使用下面的命令行精确地指定你想要的主服务器作为副本的目标:

./redis-trib.rb add-node --slave --master-id 3c3a0c74aae0b56170ccb03a76b60cfe7dc1912e 127.0.0.1:7006 127.0.0.1:7000  

这样我们就把一个新的副本赋予了一个指定的主服务器。

一种更手工的给指定主服务器添加副本的方式,是添加一个新节点作为一个空主服务器,然后使用 CLUSTER REPLICATE 命令将其变为副本。如果节点被作为从服务器添加,但是你想移动它为另一个不同的主服务器的副本,这也是可行的。

例如,为了给节点 127.0.0.1:7005 添加一个副本,这个节点当前服务 11432-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 connect  

这个 3c3a0c…的节点现在有两个从服务器了,分别运行于 7002(已存在的)和 7006(新的)端口。

移除节点(Removing a node)

要移除一个从服务器节点,只要使用 redis-trib 的 del-node 命令就可以:

./redis-trib del-node 127.0.0.1:7000 <node-id>  

第一个参数只是集群中的一个随机节点,第二个参数是你想移除的节点的 ID。

你也可以用同样的方式移除一个主服务器节点,但是,为了移除一个主服务器节点,它必须是空的。如果主服务器不是空的,你需要先将其数据重分片到其他的主服务器节点。

另一种移除主服务器节点的方式,就是在其从服务器上执行一次手工故障转移,当它变为了新的主服务器的从服务器以后将其移除。显然,当你需要真的减少你的集群中的主服务器的数量时这个没有什么帮助,如果那样的话,就需要重新分片了。

副本迁移(Replicas migration)

在 Redis 集群中,可以使用下面的命令在任何时候重新配置一个从服务器复制一个不同的主服务器:

CLUSTER REPLICATE <master-node-id>  

但是有一种特殊的场景,你想让副本集自动地从一个主服务器移动到另一个主服务器,而不需要系统管理员的帮助。自动重新配置副本集被称为副本集迁移,这可以改善 Redis 集群的可靠性。

注意:你可以在 Redis 集群规范中阅读到副本集迁移的细节,这里我们只提供一般性的信息,以及为了从中受益你该做什么。

为什么你想让你的集群副本在某些特定条件下从一个主服务器移动到另一个的原因,是通常情况下 Redis 集群对失败的抵御能力和连接到指定从服务器的副本数量成正相关。

例如,每个主服务器只有单个副本组成的集群在主服务器及其副本同时失效时就不能够继续运转,因为没有其他的实例拥有这台主服务器服务的哈希槽的副本。但是,网络断裂可能会在同一时间隔绝若干节点,其他类型的故障,例如单个节点的硬件或者软件错误,是值得注意的一类故障,很可能会同时发生,所以在每个主服务器拥有一个从服务器的集群中,有可能从服务器在下午 4 点被干掉,而主服务器在下午 6 点被干掉。这仍然会导致集群不再能运转。

为了改进系统的可靠性,我们有一些增加额外副本集到每个主服务器的选项,但是这代价昂贵。副本迁移允许添加更多的从服务器到少许的主服务器。所以你可以有 10 个主服务器,每个有一个从服务器,总共 20 个实例。但是你为某些主服务器添加例如 3 个以上的实例,那么有些主服务器会有多余一个的从服务器。

有了副本集迁移会发生什么?如果一个主服务器没有从服务器,一个来自于拥有多个从服务器的主服务器上的从服务器会迁移到这个孤独的主服务器上。所以像上面我们举的例子中,在你的从服务器下午 4 点下线以后,另一个从服务器会接替它的位置,当主服务器在下午 5 点也失效的时候,仍然有一个从服务器可以被选举,这样集群就可以继续运转了。

那么,简而言之,你应该了解副本集迁移的什么呢?

  • 集群会尝试从在某一个指定时刻拥有最多数量副本集的主服务上迁移一个副本。
  • 为了从副本迁移中受益,你需要在集群中添加多一些的副本到单个主服务器,无论是什么主服务器。
  • 有一个称为 replica-migration-barrier 的控制副本迁移特性的配置参数。你可以在 Redis 群提供的示例 redis.conf 文件中读到更多信息。

升级节点(Upgrading nodes in a Redis Cluster)

升级从服务器节点很简单,因为你只需要停止节点然后用已更新的 Redis 版本重启。如果有客户端使用从服务器节点分离读请求,它们应该能够在某个节点不可用时重新连接另一个从服务器。

升级主服务器要稍微复杂一些,建议的步骤是:

  1. 使用 CLUSTER FAILOVER 来触发一次手工故障转移主服务器(请看本文档的手工故障转移小节)。
  2. 等待主服务器变为从服务器。
  3. 像升级从服务器那样升级这个节点。
  4. 如果你想让你刚刚升级的节点成为主服务器,触发一次新的手工故障转移,让升级的节点重新变回主服务器。

你可以按照这些步骤来一个节点一个节点的升级,直到全部节点升级完毕。

迁移到 Redis 集群(Migrating to Redis Cluster)

想迁移到 Redis 集群的用户可能只有一个单一的主服务器,或者已经使用了已存在的分片布局,通过使用某种内部算法,或者他们的客户端库实现的分片算法,或者 Redis 代理,键被分拆到 N 个节点上。

这两种情况下迁移到 Redis 集群都很简单,但是最重要的细节是,如果程序使用了多键操作,怎么办。有三种不同的情况:

  1. 没有使用多键操作,或者事务,或者涉及多个键的 Lua 脚本。键被独立地访问(即使通过事务或者 Lua 脚本组合针对同样的键的多个命令一起来访问)。
  2. 使用了多键操作,或者事务,或者涉及多个键的 Lua 脚本,但是键都有相同的哈希标签(hash tag),也就是说这些一起使用的键都碰巧有相同的{…}子串。例如,下面的多键操作是在相同的哈希标签上下文中定义的:SUNION {user:1000}.foo {user:1000}.bar。
  3. 使用了多键操作,或者事务,或者涉及多个键的 Lua 脚本,但是键的名字没有一个显式的或者相同的哈希标签。

Redis 不处理第三种情况:应用程序需要被修改为不能使用多键操作,或者只能在相同的哈希标签上下文中使用。

前两种情况覆盖到了,所以我们会聚焦在这两种情况,它们会用相同的方式来处理,所以本文不会去区别对待。

假设你的已存在数据集已经被拆分到了 N 个主服务器上,如果你没有已存在的分片的话 N=1,你需要下面的步骤来迁移你的数据集到 Redis 集群:

  1. 停止你的客户端。当前没有自动在线迁移(live-migration)到 Redis 集群的可能。你也许可以通过精心策划一次在你的程序或环境上下文中的在线迁移来办到。
  2. 使用 BGREWRITEOF 命令为所有你的 N 个主服务器生成一个追加文件,然后等待 AOF 文件完全生成。
  3. 按照 aof-1 到 af-N 保存你的 AOF 文件到某处。此时愿意的话你可以停掉你的旧实例(这很有用,因为在非虚拟化的部署中,你常常需要重用这些计算机)。
  4. 创建一个由 N 个主服务器和 0 个从服务器组成的 Redis 集群。你可以稍后添加从服务器。确保所有你的节点都是使用追加文件来持久化。
  5. 停止所有的集群节点,用你已存在的追加文件替换他们的朱家文件,aof-1 替换第一节点,aof-2 替换第二个节点,一直到 aof-N。
  6. 使用新的 AOF 文件来重启你的 Redis 集群。它们会抱怨按照配置有些键不应该出现。
  7. 使用 redis-trib fix 命令来修正集群,这样键就会根据每个节点的哈希槽被迁移了。
  8. 最后使用 redis-trib check 来确保集群是正常的。
  9. 重启被修改为支持 Redis 集群的客户端。

还有一个方式从外部实例导入数据到 Redis 集群,就是使用 redis-trib import 命令。

这个命令移动一个运行实例(同时删除源实例上的键)上的所有键到一个指定已存在的 Redis 集群。但是,注意如果你使用 Redis 2.8 实例作为来源实例,操作可能很慢,因为 2.8 没有实现迁移连接缓存(migrate connection caching),所以在执行这个操作之前,你可能得重启你的 Redis 3.x 版本的源实例。

上一篇: 集群(中)