[个人翻译]Redis 集群教程(下)

水平有限,如果您在阅读过程中发现有翻译的不合理的地方,请留言,我会尽快修改,谢谢。
    

一个更有趣的示例程序

     我们上边写的那个示例程序不够好玩。他以简单的方式写入到集群而没有检查写入的正确性。
     从我们的观点看,集群接收写入命令可能每次操作总是把键foo写入 为42,并且我们一点也没有注意到。
     所以在redis-rb-cluster库内,有一个更有趣的应用程序consistency-test.rb。他使用了一组计数器,默认是1000,并且发送 INCR命令来增加计数器。
     然而除了写入命令,这个程序还做了其它的两件事:
     》当计数器被 INCR更改,这个程序将记住这次写入。
     》每次写入都会读取一个随机的计数器,并且检查他的值是否是预期的值,并和内存中的值进行对比。
 
     这意味着这是一个简单的一致性检查程序,并且这个程序会告诉你集群是否有丢失写入,或者有收到写入但没有收到确认。在第一个示例我们将看到一个计数器有一个值,这个值小于我们记住的的值,第二个示例这个值比记住的值大。
     运行consistency-test 程序,每秒种将产生一行输出:
$ 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) |

     每一行显示执行读取和写入的数字,和错误的数字(由于系统不可用,因为错误不接受查询)。

     如果发现某些不一致,将有新行被输出。这就是结果,例如,如果在程序运行中我手动的重置一个计数器:
     计数器的真实值114,当我把他的值设置为0,那么程序将记录114丢失写入操作( INCR 没有被集群记住);
     作为一个测试程序还可以更有趣,我们将使用他测试resis集群的故障转移。

     测试故障转移

     注意 :在测试期间,你要打开一个标签运行一致性测试程序 。
     为了能触发故障转移,我们能做的最简单事情(也就是在分布式系统内也可能发生的语义上最简单的故障)是崩溃单个进程,在我们的示例内是一个主节点。
     我们可以找到一个集群,并使用下面的命令使它崩溃。
     
$ 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
    
  Ok,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 Cluster教程的第一部分时说过,的由于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集群使用 CLUSTER FAILOVER命令来支持故障转移,必须在你想要故障转移的主节点的一个从节点上执行这个命令。
     手动故障转移是特殊的,并且对于实际的主节点故障导致的故障转移相比更安全,因为他们在一定程度上避免在处理过程中丢失数据,只有在系统确保新的主节点从旧的主节点处理完所有复制流操作后切换客户端从原始主节点到一个新的主节点。
     当你执行手动故障转移时你会看到如下的从节点的log:
# 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上启动一个新节点一样简单(我们已经为现有的6个节点使用了7000到7005端口)除了端口号与其它节点的配置相同,所以为了能匹配我们为之前的节点使用的设置你需要做:
     》在终端节点添加一个新标签。
     》进入cluster-test目录
     》创建一个名字为7006的目录。
     》在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

 

     正如你看到的我使用 add-node命令在第一个参数指定新节点的地址,第二个参数为任意的已存在的集群节点的地址。
     实际上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的分片功能来给新的节点分配哈希槽了。展示这些基本上是没有用的因为我们之前的章节已经做过了,这里没有什么不同,只是resharding空节点为目标。
     
     添加一个新节点做为复本
     有两种执行方式来添加一个新的复本。最好的一种方式还是使用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命令把他转换为一个复本。这种方式对于已经做为从节点的节点,想移动他作为其它主节点的复本的情况一样有效。
     例如,为保持11423-16383哈希槽的127.0.0.1:7005节点添加一个复本,他的节点ID为 3c3a0c74aae0b56170ccb03a76b60cfe7dc1912e, 所有我需要做的是连接一个新节点(已添加为空主节点)并发送下面的命令:
     
redis 127.0.0.1:7006> cluster replicate 3c3a0c74aae0b56170ccb03a76b60cfe7dc1912e

 

     
     就这样。我们为这些哈希槽添加了一个新的复本,并且在集群中的其他节点也会知道这个节点(过几秒之后需要更新他们的config).我们可以使用下面的命令进行校验:
 
$ 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-trib的del-node命令就可以移除一个从节点:
./redis-trib del-node 127.0.0.1:7000 `<node-id>`

 

 
     第一个参数是集群中的一个随机节点,第二个参数是你想移除的节点ID.
     你可以以同样的方式移除一个主节点,然而要移除的主节点必需是空的。如果主节点是非空的,你需要对他的数据进行重新分片到其他的主节点后才能移除。另一个移除主节点的可替代方案是手动执行故障转移他的一个从节点,当他变为新主节点的从节点后就可以移除了。显然这是没有帮助的当人想减少在实际 集群中的主节点数。在这里个例子中,需要分片技术。
 
     

复本迁移

     Redis集群内可能随时会重新配置一个从节点来做别一个主节点的从节点,只需要运行下面的命令:
CLUSTER REPLICATE <master-node-id>

    

     然而也有特殊的场景,你想要复本自动的从一个主节点移动到另一个节点,而不用系统管理员的帮助。这种自动的复本重新配置称为复本迁移并且可以改善集群的可靠性。
     注意:你可以在 Redis Cluster Specification阅读详细的复本迁移信息,这里我们只提供一般的概念信息以及你应该做什么才能从中获益。
     在一定条件下,你想要让集群复本从一个主节点移动到另一个主节点的原因是,通常redis集群对故障的抵抗和附加到给定的主节点的复本数一样。
     例如一个集群的每一个主节点都有一个单独的从节点,如果一个主节点和他的从节点同时出现问题那么集群将不能继续运行,只因为这里没有一份主节点所服务的哈希槽的复本。然而网络分隔(netsplits)可能会同时分离出许多节点,很多其他类型的故障,比如单个节点本地的硬盘或软件故障,是不太可能同时发生的非常显著故障类别,所以可能在你的集群中每一个主节点只有一个从节点,从节点在4点的时候被杀掉,主节点在6点的时候被杀掉,这将导致集群不能再运行。
     为了提高系统的可靠性,我们有可选的对每个主节点添加额外的复本,但是昂贵的。复本迁移允许添加更多的从节点到少数主节点,所以你有10个主节点,每个主节点有一个从节点,共有20个实例。然而你添加,例如3个实例以上,作为某些主节点的从节点,某些主节点将不只有一个从节点。
     复本迁移对于一个没有从节点的主节点发生的是,从有多个从节点的主节点和复本迁移到孤立主节点。所以在我们上面做的例子,凌晨4点你的从节点死掉后,另一个从节点会替换掉他。如果在5点的时候主节点出现故障,还有一个从节点被选为主节点,所以集群还可以持续运行。
     简而言之,对于复本迁移你需要知道的是:
     》在给定的时间,集群将尝试从有最大数量复本的主节点迁移复本。
     》为了从复本迁移中获益,你需要做的是为集群中的一个主节点添加多个从节点,无论是哪个主节点。
     》有一个配置参数控制着复本迁移功能,称为 cluster-migration-barrier:你可以阅读集群中提供的redis.conf示例来得到更多的信息。
     
    

升级集群中的节点

     升级一个从节点并不容易,由于你需要停止这个节点并且重启升级后的版本。如果有客户端使用从节点扩展读取 ,如果给给的节点不可用,他们应该能够重新连接到不同的从节点。
     升级主节点有一些复杂,推荐的步骤是:
     》使用 CLUSTER FAILOVER 触发主节点的手动故障转移到他的一个从节点(查看本文中的手动故障转移( Manual failover)章节)。
     》等待主节点变成从节点。
     》最后像升级从节点一样升级这个节点。
     》如果你想要让刚升级后的节点做为主节点,为了能把升级后的节点变回主节点需要手动触发故障转移。
     接下来的步骤你需要一个节点一个节点的升级其他的节点直到升级完所有的节点。
 
     

迁移到Redis集群

     愿意迁移到Reids集群的用户可能 只有一个主节点,或者已经使用预先存在的分片设置,使用一些内置的算法或者通过他们的客户端主库或Redis代理执行分片算法实现,将键分开到N个节点。
     这两种情况都可以很容易的迁移到Redis集群,然而最重要的细节是通过程序使用多键值操作,和如何操作,现在有三种不同的情况。
     1.不使用 多键操作,或者事务,或者Lua角本调用多个键,键是独立访问的(即使通过事务或者Lua角本组合多个命令,相关同一个键在一起访问)。
     2.只有键有相同哈希标签(hash tag)才使用多键操作,事务,或者涉及多个键的Lua角本,这意味着在一起使用的键都有一个相同的{...}子串。例如下面的多个键操作在上下文定义了相同的的哈希标签:
     3.使用多键操作,事务,或者涉及多个键的Lua角本,而没有明确的或相同的哈希标签。
     这三种情况不是Redis集群处理的:为了不使用多值操作或者只使用上下文有相同哈希标签的,需要修改应用程序 。
     情况1和情况2包含在内, 我们将集中说明这两种情况,他们是以同样的方式处理的,所以在文档内不区别对待。
     假设你已经有预存在的数据分片到N个主节点,如果你没有预分版本N=1,为了把数据迁移到Redis集群你需要下面的步骤:
     1.停止你的客户端。现在还不能自动在线迁移到Redis集群。你可以在你的应用程序或环境的上下文策划来做时间迁移。
     2.使用 BGREWRITEAOF 命令为你的N个主节点生成一个追加式文件,并且等待AOF文件完全生成。
     3.从aof-1到aof-n把你的AOF文件保存在一个地方。这个时刻你可以根据自己的意愿来停止旧的实例(这是非常有用的,因为在非虚拟部署你经常需要重用相同的电脑)。
     4.创建一个由N个主节点0个从节点组成的集群。等会儿再添加从节点。确保你所有的节点都使用追加式文件做持久化。
     5.停止所有的集群节点,使用你预存在的追加式文件替换集群的。aof-1对应第一个节点,aof-2对应第二个节点,至到aof-N.
     6.使用新的AOF重启你的Redis集群。他们会抱怨根据他们的配置在这里不应该有键的存在。
     7.使用 redis-trib fix 命令来修复集群,将根据每个节点的哈希槽是否有权威性来迁移键。
     8.最后使用 redis-trib check来检查你的redis集群是否ok.
     9.重启你已修改使用Redis集群感知的客户端库的客户端。
 
     还有一另一种替代方案从外部实例导入数据到Redis集群,使用 redis-trib import命令。
     这个命令移动运行实例的所有键(删除源实例的键)到指定的已存在的Redis集群。然而需要注意的是如果你使用的是Redis2.8作为源实例这个操作可能会很慢,因为2.8没有实现迁移连接缓存,所以你可能会在执行这个操作前使用Redis.x版本重启你的源实例。
posted @ 2017-02-16 10:05  li-peng  阅读(1237)  评论(1编辑  收藏  举报