www.icesr.com
IT运维工程师的摇篮

NoSQL之Redis—集群教程

[不忘初心]

在通常情况下,Redis单机服务能够解决一般应用场景,但随着互联网数据的爆炸式增长,缓存的大小及组织方式也需要相对应的调整。本文源自于官方文档对于集群教程的通俗解释,我们先一起学习一下。马上开始我们的正文部分吧。

——————————————————————————————————————————————————–

本文是Redis集群教程的入门教程,本教程不包含复杂难懂的分布式概念。仅仅介绍了如何搭建,测试,操作集群的方法,以及从用户的角度出发观察整个系统的运行行为。这其中不包含在Redis集群规范中包含的细节内容。

然而,本教程从最终用户的角度,尽可能的提供关于Redis集群在可用性和一致性特征方便的信息,并且用一种简单易于理解的方式来说明。

值得注意的是:本教程适用于Redis 3.0 或者更高的版本。

如果你打算深入的学习Redis集群的部署方法,虽然不是强制的,还是推荐你在阅读完这篇教程之后,再去看看集群规范的内容。然而,一个比较好的做法是先从本文开始学习,之后建立一个Redis集群练习一下,再阅读集群规范的相关内容。


集群简介(Redis Cluster 101

Redis集群提供了一种可以在多个Redis节点之间进行数据自动共享的设施。
Redis集群同时提供了利用分区提供一定程度的可用性,这样在实际项目中,Redis集群有能力在当一些节点失效,或者失去连接时仍然能够继续处理命令请求。然而,集群将会在更大的错误(如master节点不可用)时,集群变得不可用。
因此,在实际项目中,我们能够Redis集群中获取到如下的好处:
  • 将数据集自动的拆分到多个节点的能力。
  • 当集群中的一部分节点失效或者无法与其他节点连接时,仍然能够继续处理请求命令的能力。

Redis集群的TCP端口

每一个Redis集群的节点都要求保持两个TCP连接开启。常规的Redis TCP端口用来对客户端提供服务,如6379,在此基础上加上10000获得第二个数据端口,如16379。这个16379端口被用来作为集群总线,这是一个点对点连接的信道,其使用二进制协议。集群总线被用来进行故障监测,配置更新,失效授权等等。客户端在原则上不会使用集群总线进行数据交换,而是使用常规的端口(6379)进行。因此,在建立Redis集群时,一定要确保在防火墙中同时开启了这两个端口,否则的话,Redis集群节点将不能够进行正常的链接。
命令端口与集群总线端口之间的差值是固定的,并且一直是10000.
请记住下面这些Redis集群的约定,其能够让你的Redis集群正确的运行,如下:
  • 正常的客户端通信端口(默认6379)用来与客户端进行通讯,并向所有链接到集群的客户端开放,再加上其他集群节点(使用客户端端口做数据迁移时使用)
  • 集群总线端口(16379)不许能够被其他集群节点可以访问到。
如果你没有同时打开这两个端口,那么你的集群将不会按照期望来运行。
集群总线使用一个不同痛的,二进制协议,在节点与节点之间进行数据交换。这个二进制协议更加适合小带宽与处理时间的场景下进行信息交换。

Redis集群与Docker(本内容有兴趣的看官先自行学习)


Redis集群数据共享

Redis集群没有使用一致性的hash进行分片,而是使用了另外一种我们称之为哈希槽的方法,使得每一个key都能在其中。
一个Redis集群拥有16384个哈希槽,集群使用计算公式计算出一个给定的key所处的哈希槽,我们使用简单的方法:key关于CRC16的16384的模。
集群中的每一个阶段负责处理一部分哈希槽,举个例子,在三个节点的集群中,如下:

  • 节点A负责处理0号到5500号哈希槽
  • 节点B负责处理5501号到11000号哈希槽
  • 节点C负责处理11001号到16384号哈希槽
这种做法允许轻易的增加或者删除集群中的节点。举个例子:如果你想要在集群中增加一个新节点D,那么集群只需要将A,B,C三个节点中的一部分哈希槽转移到D当中即可。类似的,当想要移除节点A时,那么只需要将节点A服务的哈希槽转移到B,C中,当节点A服务的哈希槽变为空时,我们就可以彻底的从集群中移除节点A。
因为讲一个哈希槽从一个节点转移到另一个节点是不需要阻塞操作的,因此,增加,删除,改变数量都不会引起下线。
Redis集群支持多个key的操作,只要这些key位于同一个简单命令的执行过程(或者整个交换,或者LUA脚本执行)都属于同一个哈希槽。用户可以通过使用哈希tags来强制多个key成为同一个哈希槽的一部分。
哈希tag在Redis集群规范中有详细描述,其大意是:如果存在一个key的substring中存在于“{ }”之间,只有括号其中的字符串才会被进行hash,举个例子:“{foo}”这个字符串了另外一个包含“{foo}”字符串会被放置到同一个哈希槽当中,其也可以被一起作为某些多参数命令的实参。

Redis的主从复制模式

为了在Redis集群中的某些master节点失效,或者无法与主节点进行通信的场景下,仍然保持集群的可用性,Redis集群使用一个主从复制模式,集群中的每个节点都拥有1到N个复制。
在我们之前所举的A,B,C例子中,如果B节点从集群中失去链接,导致我们失去了获取5501到11000号哈希槽的服务。然而,当我们创建集群时(或者创建之后)我们为每一个master节点创造一个slave,使得最终的集群有A,B,C三个master节点,A1,B1,C1三个slave节点构成,由此,在节点B失效时,集群将B1设置为新的主节点,代替了原来的B,继续处理5501到11000号哈希槽的服务。这样就使得集群能够继续正常的提供服务。
但是,特别提醒的是:如果B,B1都同时失效了,那么Redis集群就不能够再继续提供服务了。

Redis集群的一致性保证

Redis集群并不保证数据的强一致性。在实际的项目中意味着,特定的情况下,Redis集群有可能丢失已经被执行过的写命令。
第一个原因是:异步复制。这意味着在写操作过程中发生下面的事情:
客户端想主节点B发送一条写命令。
主节点B回复OK到客户端
主节点B将刚刚执行写命令复制给其slave节点B1,B2,B3
正如你所看到的,主节点B回复动作,发生在等待B1,B2,B3回复之前,因为等待将会对Redis造成高昂的延迟等待,因此,如果你的客户端写入数据,主节点B接受写命令,但是主节点B却在发送写命令之前失效了,那么刚刚上提升为master的slave节点(没有接受到写命令)将会永远的丢失这条写命令。
这种现象和将大多数数据库配置为每秒进行数据保存到磁盘的效果类似,因此,这样的场景,你已经能够推出:因为过去的不涉及分布式系统的传统数据库经验。同样的,你也可以通过强制数据库在回复客户端之间必须先将数据持久化到磁盘来提升数据一致性,但是这种做法的结果将极大的降低性能,这种做法同样等于是在Redis集群中进行同步复制。
基本上,在性能与一致性之间存在着一个平衡点。
当确实需要一致性时,redis集群也支持同步写操作(做法是:通过WAIT命令),这样就不带可能会丢失写操作,但是,请记住:Redis集群没有实现强一致性即使使用同步复制。在更多导致失败的复杂场景下,仍然可能的是,一个slave节点没有接受到其master节点的写命令。
另外一个值得注意的Redis集群会丢失写的场景是:集群的分片网络中出现一个客户端至少与一个主节点在内的少数实例中被孤立起来。
举个例子:假设我们给出6个节点,A,B,C,A1,B1,C1,三主,三从。另外还有一个客户端Z1。
当集群中发生网络分片时,可能出现的场景是,其中一方由A,C,A1,B1,C1组成,另外一方由B,Z1构成。
此时,Z1仍然有能力对B进行写操作。如果集群网络分片的时间很短,那么集群会继续正常运行。然而,如果分片网络持续的时间足够使得B1被提升为新的master节点,那么此时,Z1发送到B的写操作将会丢失。
请注意:这里存在着一个Z1向B发送写操作的最大窗口限制,如果在大多数节点的分片网络的那一方,有足够的时间将一个slave提升为master,那么包含大多数节点的分片网络中的每一个master节点都将停止接受写命令。
这个最大的时间是Redis集群配置中一个非常重要的配置参数,被称之为节点超时时间。
  • 当超过该时间限制时,master节点会被是为失效,并且将会被其备份的slave所取代。
  • 类似的,当超时之后,如果没有一个主节点能感知到大多数节点网络中的其余的主节点,那么将返回一个错误,并且停止接受写操作。

Redis集群配置参数

我们即将要创建一个集群部署的示例。在此之前,我们一起来看看Redis配置文件redis.conf中关于集群的配置参数有哪些。其中一些参数的含义是非常明显的,起的参数,我们将清楚地介绍使得你能够继续阅读。
  • cluster-enable<yes/no>:如果是yes的话,将会使用一个特殊的Redis实例并且支持Redis集群,否则的话,像通常情况下一样,只会启动一个单独的Redis实例。
  • cluster-config-file<filename>:请记住:尽管这是一个可配置项,但是这并不是一个有用户进行编辑的文件。但是,Redis集群节点能够在每次在此配置变化时,自动的保留集群配置(状态),为的是能够在重启时进行载入。这个文件中,记录了集群中其他节点的状态,持有的变量等信息。这个文件通常是作为接受到的消息的结果时,进行重写和保存至磁盘。
  • cluster-node-timeout<milloseconds>:Redis集群节点的最大是失效时间,超过这个限制节点将会被视为失效。如果一个master节点在给定的时间之内没有重新链接到集群中,其将会被视为失效,并且被slave节点替代。这个参数控制了Redis集群中其他非常重要的功能。请记住:集群中的每一个几点在给定的时间之内,如果没有链接到主体的master节点网络中,其将不再接受命令、
  • cluster-slave-validity-factor<factor>:如果设置为0,slave将会永远在master发生故障时进行替换,而忽略master节点与slave节点中仍然持续的断开时间。如果这个数字是一个正数,最大的断开时间计算方法为节点的失效时间(见上一条)乘以这个正数。并且如果这个节点是slave节点,其将不会在超过给定的短线时间后对master节点进行替换。举个例子:如果节点的失效时间为5s,该参数配置为10,那么slave节点将在50s之后不会对master节点进行替换。特别提醒:当这个值不是0时,都有可能导致Redis集群中发生故障的master无法找到一个slave来替换,在这种情况下,只有当该master重新连接的情况下才能让整个Redis集群节点恢复其可用性。
  • cluster-migration-barrier<count>:master节点持有的最小的slave连接数,使得其余的slave提升为master时不会被任何slave节点覆盖。在下文的复制迁移部分我们将更加详细的介绍这个配置项的功能。
  • cluster-require-full-coverage<yes/no>:如果设置为yes,也是默认的,集群节点将会在只能接受部分keys时,停止接受写命令。如果设置为no,尽管请求只能处理一个keys的子集,集群节点仍然能够提供服务。

创建并使用Redis集群

注意:为了手动部署一个可用的Redis集群,学习它的某些配置项是非常重要的。然而, 如果你想要启动一个Redis集群并且快速的跳过本节内容,建议直接使用Redis创建脚本来快速的创建一个Redis集群。
本节中,为了创建一个Redis集群,我们需要做的第一步就是创建一些集群中运行的空的Redis节点。这意味着集群中没有使用普通的Redis客户端,而是使用一个特殊的模式,其需要通过配置变为一个特殊节点来完成,由此使得Redis集群中的节点能够拥有集群特性并且接受集群命令。
以下是一个包含了最少选项的集群配置文件示例:
port 7000
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
appendonly yes

正如你所看到的,cluster-enabled选项用于开启节点的集群模式。每一个Redis示例都包含有一个文件路径:关于这个节点的配置文件的位置,默认情况下是nodes.conf。这个文件无需我们手动进行配置,其是在Redis启动是创建的,并且在需要的时刻进行自动更新。

注意:在Redis集群节点中,最少需要包括3个master主节点,不过在你第一次试用Redis集群的功能是,我们强烈的建议你使用6个节点,即3主,3从的结构。
为了达到这个目的,首先进入一个目录,创建以6个端口命名的子目录,之后我们将在6个子目录上都开启一个Redis实例。创建命令如下:
mkdir cluster-test
cd cluster-test
mkdir 7000 7001 7002 7003 7004 7005

在7000到7005每一个目录中都创建要给redis.conf文件,文件的内容可以使用上文刚刚介绍的最小配置项,但是,请记住,一定要确保配置文件中的命名,与文件目录结构保持一致。

现在,从Redis Github页面的unstable分支中取出最新的源码,编译出可执行文件redis-server,并将文件复制到cluster-test文件夹中,然后使用如下的命令,打开6个终端窗口。命令示例如下:

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

正如你所见的,每一个示例都会打印出运行日志,因为node.conf文件不存在,所以每一个节点都会创建一个新的ID:

[82462] 26 Nov 11:56:55.329 * No cluster configuration found, I&#39;m 97a3a64667477371c4479320d683e4c8db5858b1

对应的实例将会一直使用这个ID值,从在整个Redis集群中唯一标识自己。每个节点通过这个ID值来识别其他节点,而不是通过IP或端口。因为,IP或端口都可能会改变,但是这个ID在整个节点的生命周期内都是不会变化的。因此,我们将这个ID值成为节点ID


创建集群

现在我们已经创建了6个正在运行中的Redis集群实例,接下来我们需要通过写入一些集群配置来创建我们的Redis集群。
通过使用Redis集群命令行工具redis-trib,我们能够很快的编写完成整个节点的配置文件,这个工具是一个Ruby程序,其能够执行一些在Redis实例上执行特殊的命令,来创建一个新集群,检查,重组一个存在的集群网络等等。这个redis-trib在src目录中,你需要安装Redis整个包才能够运行redis-trib:
gem install redis

执行以下命令来创建集群:

./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

命令的解释如下:

  • 给定的命令将会被ruby程序翻译为create,这表示我们想要创建一个新的集群。
  • 选项–replicas 1意思是为每一个master节点创建一个slave节点。
  • 其他参数表示redis实例的地址及端口,以空格为间隔。
简单的来讲,这个命令的意思就是按照需求启动了3主,3从的redis集群
Redis-trib将会打印出一份配置单,
&gt;&gt;&gt; Creating cluster
Connecting to node 127.0.0.1:7000: OK
Connecting to node 127.0.0.1:7001: OK
Connecting to node 127.0.0.1:7002: OK
Connecting to node 127.0.0.1:7003: OK
Connecting to node 127.0.0.1:7004: OK
Connecting to node 127.0.0.1:7005: OK
&gt;&gt;&gt; Performing hash slots allocation on 6 nodes...
Using 3 masters:
127.0.0.1:7000
127.0.0.1:7001
127.0.0.1:7002
127.0.0.1:7000 replica #1 is 127.0.0.1:7003
127.0.0.1:7001 replica #1 is 127.0.0.1:7004
127.0.0.1:7002 replica #1 is 127.0.0.1:7005
M: 9991306f0e50640a5684f1958fd754b38fa034c9 127.0.0.1:7000
slots:0-5460 (5461 slots) master
M: e68e52cee0550f558b03b342f2f0354d2b8a083b 127.0.0.1:7001
slots:5461-10921 (5461 slots) master
M: 393c6df5eb4b4cec323f0e4ca961c8b256e3460a 127.0.0.1:7002
slots:10922-16383 (5462 slots) master
S: 48b728dbcedff6bf056231eb44990b7d1c35c3e0 127.0.0.1:7003
S: 345ede084ac784a5c030a0387f8aaa9edfc59af3 127.0.0.1:7004
S: 3375be2ccc321932e8853234ffa87ee9fde973ff 127.0.0.1:7005
Can I set the above configuration? (type &#39;yes&#39; to accept): yes

如果你接受自动创建的集群配置,那么输入yes,集群就会完成配置并且进行相互连接。

&gt;&gt;&gt; Nodes configuration updated
&gt;&gt;&gt; Sending CLUSTER MEET messages to join the cluster
Waiting for the cluster to join...
&gt;&gt;&gt; Performing Cluster Check (using node 127.0.0.1:7000)
M: 9991306f0e50640a5684f1958fd754b38fa034c9 127.0.0.1:7000
slots:0-5460 (5461 slots) master
M: e68e52cee0550f558b03b342f2f0354d2b8a083b 127.0.0.1:7001
slots:5461-10921 (5461 slots) master
M: 393c6df5eb4b4cec323f0e4ca961c8b256e3460a 127.0.0.1:7002
slots:10922-16383 (5462 slots) master
M: 48b728dbcedff6bf056231eb44990b7d1c35c3e0 127.0.0.1:7003
slots: (0 slots) master
M: 345ede084ac784a5c030a0387f8aaa9edfc59af3 127.0.0.1:7004
slots: (0 slots) master
M: 3375be2ccc321932e8853234ffa87ee9fde973ff 127.0.0.1:7005
slots: (0 slots) master
[OK] All nodes agree about slots configuration.
最后,如果所有命令都正确的执行的话,你将会看到下面这样的一个返回信息:
[OK] All 16384 slots covered

这表示集群中的16384个哈希槽都会有一个明确的master节点来处理,集群就能够正常的运作了。


与集群交互

在本小节,Redis集群存在一个问题:缺少客户端库的实现。以下是我所知道的实现方式:
redis-rb-cluster:是一个ruby是实现的程序,由@antirez编写,用于其他实现的参考。该实现是对redis-rb的一个简单包装,高效的实现了与集群进行交互的最少语义
  • redis-py-cluster:看上去是redis-rb-cluster的Python版本。支持redis-py的大多数功能。并且其仍在开发之中。
  • Predis:流行的Predis对redis的集群提供支持,其最近也获得更新并且也在开发之中。
  • Jedis:最流行的Java客户端,其最近也增加了对redis集群的支持,关于Jedis集群的内容请在其project的readme部分学习。
  • Redis unstable分支中的redis-cli程序实现了非常基本的集群支持,可以使用redis-cli -c来启动。
  • 更多支持内容,请各位看官在官方文档中了解学习。
测试Redis集群比较简单的办法就是使用上面我们介绍的客户端或者使用redis-cli命令行工具。接下来我们以redis-cli为例进行演示:
$ redis-cli -c -p 7000
redis 127.0.0.1:7000&gt; set foo bar
-&gt; Redirected to slot [12182] located at 127.0.0.1:7002
OK

redis 127.0.0.1:7002&gt; set hello world
-&gt; Redirected to slot [866] located at 127.0.0.1:7000
OK

redis 127.0.0.1:7000&gt; get foo
-&gt; Redirected to slot [12182] located at 127.0.0.1:7002
&quot;bar&quot;

redis 127.0.0.1:7000&gt; get hello
-&gt; Redirected to slot [866] located at 127.0.0.1:7000
&quot;world&quot;

注意:如果你是通过脚本来创建redis集群,那么你的集群可能监听不同的端口,默认从30001开始。

redis-cli对集群的支持是非常基本的,所以其总是依靠Redis集群节点的自动校准来重定向到正确的节点上。一个真正意义上的客户端应该比这种方法做的更好,并且换证哈希槽与节点地址之间的映射关系,从而直接使用正确的命令到达正确的节点上。这个映射关系只有在修改某些配置时,才会刷新。比如:在一次故障转移之后,或系统管理员增加删除节点导致集群网络结构变化之后,等等。

使用redis-rd-cluster来编写一个示例应用

在继续演示如何操作一个Redis集群之前,如故障转移,重新分片等。我们需要创建一个示例应用或者至少去了解与一个简单的Redis集群客户端进行交互的语义。
在运行示例应用的过程中,我们同时将会尝试让节点变为失效,或重新分片,以此来观察Redis集群在真实环境下的行为表现。并且为了让这个示例变得尽可能的有实际意义,我们会让这个应用向集群进行写操作。(原文是: It is not very helpful to
see what happens while nobody is writing to the cluster.
本节将通过两个示例应用来展示redis-rb-cluster的基本用法。下面是第一个示例,example.rb文件,其包含在redis-rb-cluster项目中:
require &#39;./cluster&#39;
     
      startup_nodes = [
          {:host =&gt; &quot;127.0.0.1&quot;, :port =&gt; 7000},
          {:host =&gt; &quot;127.0.0.1&quot;, :port =&gt; 7001}
      ]
      rc = RedisCluster.new(startup_nodes,32,:timeout =&gt; 0.1)
     
       last = false
    
     while not last
         begin
              last = rc.get(&quot;__last__&quot;)
             last = 0 if !last
         rescue =&gt; e
            puts &quot;error #{e.to_s}&quot;
            sleep 1
          end
      end
    
      ((last.to_i+1)..1000000000).each{|x|
          begin
              rc.set(&quot;foo#{x}&quot;,x)
              puts rc.get(&quot;foo#{x}&quot;)
              rc.set(&quot;__last__&quot;,x)
          rescue =&gt; e
              puts &quot;error #{e.to_s}&quot;
          end
          sleep 0.1
      }

这个应用做了一件非常简单的事情:它不断的用foo<number>为key,number为value。因此,如果你运行这个程序的话,应用将按照下面的顺序执行命令:

  • SET foo0 0
  • SET foo1 1
  • SET foo2 2
  • And so forth…

这个程序看起来要比它所做的功能复杂的多,因为,其被设计为当发生错误时,将错误展示到终端界面上而不是因为异常退出,于是,每一个集群执行的操作都使用一个begin rescue代码块包裹起来。

代码的第7行,有一个非常有趣的地方,它创建了一个Redis集群对象,参数含义如下:
  • 第一个参数:记录启动节点的startup nodes列表,列表中包括两个集群节点的地址。
  • 第二个参数:指定了对于集群中的各个不同的节点,Redis集群对象可以获得的最大连接数。
  • 第三个参数:timeout指定了一个命令在指定多久之后,才会被看作是执行失败。
特别的:启动节点列表(startup nodes)并不需要包含所有集群节点的地址,但是重要的是:这些节点中至少有一个是有效的,即能够被访问的。同时,请记住,redis-rb-cluster一点更写了成功连接上集群中的某个节点时,节点列表就会被自动更新。任何其他真正意义的客户端都会这么做。
现在,程序创建的Redis集群对象实例被保存到了rc变量中,我们可以将这个对象当作Redis的普通对象来使用。
在第11行到19行:当我们重新启动示例应用时,我们并不想从foo0开始,因此,我们要先在Redis中保存计数器。上面的代码被设计为在重新启动时读取这个计数器的值,如果计数器不存在的话,那么其从0重新开始。
然而,我们注意到,这是一个while循环,因为我们想要不断的尝试读取计数器的值,即使集群已经下线或者返回一个错误。在一般的应用程序中,并不需要这种如此谨慎小心的做法。
代码第21行到30行,是程序的主循环,这个循环负责设置键值对,或者,返回错误信息。
注意,我们在循环的末尾添加了一个sleep调用,让写操作的执行速度变慢,帮助执行者观察程序输出。当然,你可以在你的程序中移除这个sleep调用,来使得集群的写操作的速度尽可能的快。(与之对应的,这是一个没有真正并行流的快速的循环,所以,在最好的环境下,你能够得到大概10k ops每秒的速度)
执行example.rb程序将产生一下输出:
ruby ./example.rb
1
2
3
4
5
6
7
8
9
^C (I stopped the program here)

这个应用程序并不是十分有趣,稍后我们会看到一个更加意思的集群应用实例,不过在此之前,我们需要再使用这个例子来观察重新分片场景下,程序的运行情况。


对集群的重新分片

现在,我们来试试对进群进行重新分片操作。为了这个目的,我们需要让example.rb程序处于运行状态,之后你就可以看到这里是否会对程序的执行过程产生影响。同时,你也可以考虑注释(comment)sleep命令,让重新分片操作在近乎真实的写负载下执行。
重新分片操作基本的含义就是将某些节点上的哈希槽移动到另外一些节点上面,和创建集群一样,重新分片可以使用redis-trib程序来执行。
执行以下命令就可以开始一次重新分片操作:
./redis-trib.rb reshard 127.0.0.1:7000

你只需要指定集群中其中一个节点的地址,redis-trib就会自动找到集群中的其他节点。

目前,redis-trib只能在管理员的协助下完成重新分片的工作,要让redis-trib自动的将哈希槽从一个节点移动到另一个节点,目前还做不到。(不过这个功能并不难)。所以,从这个问题开始,第一件事就是你需要进行多大程度上的重新分片:
How many slots do you want to move (from 1 to 16384)?

我们可以尝试重新分片1000个哈希槽,如果example程序在没有sleep命令下还在运行的话,其中应该包含不少数量的keys。

然后,Redis-trib还需要知道重新分片的目标,即是,负责接收这1000个哈希槽的节点。指定目标需要使用节点的ID,而不是IP地址和端口。在这里,我们将会使用第一个master节点来作为目标,即127.0.0.1:7000。这个ID值已经在redis-trib会打印出节点列表,但是这里我们将使用下面命令使其返回ID值:
$ redis-cli -p 7000 cluster nodes | grep myself
97a3a64667477371c4479320d683e4c8db5858b1 :0 myself,master - 0 0 0 connected 0-5460

好了,我们的目标节点ID是: 97a3a64667477371c4479320d683e4c8db5858b

接着,redis-trib会向你询问要从那个节点中取出1000个哈希槽,并将这些槽移动到目标节点上。如果我们不打算从特定的节点上取出指定数量的哈希槽,那么可以向redis-trib输入all,这样的话,集群中所有的master节点都会成为源节点,redis-trib将会从各个源节点中各取出一部分,凑够1000个,然后移动到目标节点上。
$ ./redis-trib.rb reshard 127.0.0.1:7000
...
Please enter all the source node IDs.
Type &#39;all&#39; to use all the nodes as source nodes for the hash slots.
Type &#39;done&#39; once you entered all the source nodes IDs.
Source node #1:all

在重新分片的过程中,你就会看到example程序的运行对集群网络没有造成任何影响。如果你想要再次验证的话,那么可以在重新分片的过程中多次停止或重新启动example程序来验证这一点。

输入 all 并按下回车之后, redis-trib 将打印出哈希槽的移动计划,
如果你觉得没问题的话, 就可以输入 
yes 并再次按下回车:

$ ./redis-trib.rb reshard 127.0.0.1:7000
...
Moving slot 11421 from 393c6df5eb4b4cec323f0e4ca961c8b256e3460a
Moving slot 11422 from 393c6df5eb4b4cec323f0e4ca961c8b256e3460a
Moving slot 5461 from e68e52cee0550f558b03b342f2f0354d2b8a083b
Moving slot 5469 from e68e52cee0550f558b03b342f2f0354d2b8a083b
...
Moving slot 5959 from e68e52cee0550f558b03b342f2f0354d2b8a083b
Do you want to proceed with the proposed reshard plan (yes/no)? yes

输入 yes 并使用按下回车之后, redis-trib 就会正式开始执行重新分片操作,
将指定的哈希槽从源节点一个个地移动到目标节点上面:

$ ./redis-trib.rb reshard 127.0.0.1:7000
...
Moving slot 5934 from 127.0.0.1:7001 to 127.0.0.1:7000:
Moving slot 5935 from 127.0.0.1:7001 to 127.0.0.1:7000:
Moving slot 5936 from 127.0.0.1:7001 to 127.0.0.1:7000:
Moving slot 5937 from 127.0.0.1:7001 to 127.0.0.1:7000:
...
Moving slot 5959 from 127.0.0.1:7001 to 127.0.0.1:7000:
在重新分片之后,你可以使用下面的命令来测试集群是否正常:
$ ./redis-trib.rb check 127.0.0.1:7000
Connecting to node 127.0.0.1:7000: OK
Connecting to node 127.0.0.1:7002: OK
Connecting to node 127.0.0.1:7005: OK
Connecting to node 127.0.0.1:7001: OK
Connecting to node 127.0.0.1:7003: OK
Connecting to node 127.0.0.1:7004: OK
&gt;&gt;&gt; Performing Cluster Check (using node 127.0.0.1:7000)
M: 9991306f0e50640a5684f1958fd754b38fa034c9 127.0.0.1:7000
slots:0-5959,10922-11422 (6461 slots) master
M: 393c6df5eb4b4cec323f0e4ca961c8b256e3460a 127.0.0.1:7002
slots:11423-16383 (4961 slots) master
S: 3375be2ccc321932e8853234ffa87ee9fde973ff 127.0.0.1:7005
slots: (0 slots) slave
M: e68e52cee0550f558b03b342f2f0354d2b8a083b 127.0.0.1:7001
slots:5960-10921 (4962 slots) master
S: 48b728dbcedff6bf056231eb44990b7d1c35c3e0 127.0.0.1:7003
slots: (0 slots) slave
S: 345ede084ac784a5c030a0387f8aaa9edfc59af3 127.0.0.1:7004
slots: (0 slots) slave
[OK] All nodes agree about slots configuration.
&gt;&gt;&gt; Check for open slots...
&gt;&gt;&gt; Check slots coverage...
[OK] All 16384 slots covered.

上面的最后一行显示,集群运行正常。

需要注意的是:在三个主节点中,节点127.0.0.1:7000包含了6461个哈希槽,而节点7001和节点7002都只包含了4961个哈希槽,因为其两者都将自己的500个哈希槽移动到了节点7000上。
脚本化重新分片操作
重新分片能够自动的执行而不用使用互动的方式,每次手动的输入参数。可以使用如下的命令实现:
./redis-trib.rb reshard --from &lt;node-id&gt; --to &lt;node-id&gt; --slots &lt;number of slots&gt; --yes &lt;host&gt;:&lt;port&gt;

如果你经常的需要重新分片的话,使用这条命令实现里重新分片过程的自动化。然而,现在,redis-trib还没有办法自动的再平衡集群节点中key的分布和按照需求,智能的移动这些哈希槽。这个功能将会在以后加入进来。


一个更加有趣的示例应用

我们前面使用的example程序并不是十分的好。因为其只是不断的集群进行不断的写入操作,而没有检查写入操作的命令是否正确。举个例子:集群可能会错误的将example程序发送的set命令都改为set foo 42,但是因为example没有检查写入后的值,我们将不会意识到错误的发生。
因为这个原因,在redis-rb-cluster项目中包含了一个十分有趣的应用:consistency-test.rb。其使用了多个计数器(默认1000个),并且通过发送INCR命令来增加这些计数器的值。
然而在计数器增加的同时,这个应用还执行了两件额外的工作:
  • 每次使用INCR命令更新一个计数器时,应用都会记录下计数器执行的INCR命令。
  • 在每次发送INCR命令之前,程序会随机的从集群中读取一个计数器的值,并将与自己记录的值进行对比,看两个值是否相同
换句话说,这个程序是一个简单的一致性检查器,并且它还能反馈给你:如果集群丢失了某一条INCR命令,或如果其接受了某条客户算没有确认的INCR命令。对于第一种情况,consistency-test记录到计数器counter的值大于记录的命令的数量,而对于第二中情况,计数器counter的值会小于记录的命令的数量。
运行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) |

每行输出都打印了程序执行的读取次数和写入次数,以及执行操作的过程中因为集群不可用而造成的错误数量。

如果程序发现了不一致,那么它将在末尾打印出不一致的详细内容。举个例子,如果我们在consistency-test执行过程中,手动的修改了某个计数器的值:
$ redis 127.0.0.1:7000&gt; set key_217 0
OK

那么consistency-test将会像我们报告错误:

(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 |

在我们修改计数器值的时候,计数器的正确值是114.但因为我们将计数器的值修改为0,所以程序报告说丢失了114个命令。

因为这个程序具有一致性检查的功能,所以我们用它来测试Redis集群故障转移操作。

故障转移测试

注意:在本节测试内容中,你应该让consistency-test程序一直在后台运行。
为了触发一次故障转移,最简单的办法就是让集群中的某个master节点进入下线状态。
首先使用下面的命令列出集群中所有的主节点信息:
$ 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是主节点,然后我们通过向7002端口发送DEBUG
SEGFAULT命令,让这个主节点崩溃:

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

现在,我们将看到consistency-test程序将会报告大量的错误信息:

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集群并不能保证数据的强一致性。

现在,我们来使用cluster nodes命令,查看集群在故障转移操作之后,主节点的分布情况:(注意,与此同时,示例中重新启动了7002节点,以便其作为slave重新加入到集群当中)
$ 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

现在,集群中的master主节点为7000,7001,7005,其中7005就是因为7002节点下线才变为主节点的。

CLUSTER NODES的命令看起来有点吓人,但实际上是非常简单的,其组成部分为:

  • 节点ID:如 3fc783611028b1707fd65345e763befb36454d73 。
  • ip:port:节点的 IP 地址和端口号, 例如 127.0.0.1:7000, 其中 :0 表示的是客户端当前连接的
    IP 地址和端口号。
  • flags :节点的角色(例如 master 、 slave 、 myself )以及状态(例如 fail ,等等)。
  • 如果节点是一个从节点的话, 那么跟在 flags 之后的将是主节点的节点
    ID : 例如 
    127.0.0.1:7002 的主节点的节点 ID 就是3c3a0c74aae0b56170ccb03a76b60cfe7dc1912e 。
  • 集群最近一次向节点发送 PING 命令之后,
    过去了多长时间还没接到回复。
  • 节点最近一次返回 PONG 回复的时间。
  • 节点的配置纪元(configuration epoch):详细信息请参考 Redis
    集群规范
     。
  • 本节点的网络连接情况:例如 connected 。
  • 节点目前包含的槽:例如 127.0.0.1:7001 目前包含号码为 5960 至 10921 的哈希槽。

手动故障转移

有时在一个没有故障发生的主节点上进行故障转移是非常有实际用处的。举个例子:为了提升其中一个为master主节点的过程,一个好的做法是使用故障转移,目的是,使用最小影响可用性的代价将其变为slave。
使用CLUSTER FAILOVER命令可以在Redis集群中进行手动故障转移,注意,你必须对你想要转移的master节点的slave上执行该命令手动故障转移是非常特殊的,并且相对实际master节点失效导致的故障转移安全的多,因为,进行客户端从旧的master节点迁移到新的master节点上时,只有当系统已经确定新的master节点负责处理从旧的master节点执行复制流才会执行,其避免了在整个过程中数据丢失。
下面是你在执行手动故障转移时能够看到的服务日志:

# 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&#39;m the new master.

基本上,客户端链接到master节点上进行故障转移是停止的。与此同时,master节点发送其复制偏移量给其slave节点,slave在等待偏移量到达。当复制偏移量到达slave节点时,开始执行故障转移过程,并且旧的master节点被告知进行配置切换。当个客户端在旧的master节点上完成操作之后,slave将重定向到新的master节点上。


添加新节点到集群中

根据新建节点的种类,我们需要两种方法来讲新节点添加到集群中:
  • 如果要添加一个新节点是master主节点,那么我们需要创建一个空节点,然后将某些卡系统移动到这个空节点中。
  • 如果要添加一个新节点是slave节点,我们需要将这个主节点设置为集群中某个节点的副本。
上面的两种情况中,第一步都是:增加一个空节点。
启动一个新节点和我们之前创建的6个节点方法一样,使用的配置文件也一样,除了端口号改为7006.以下是其详细步骤,你需要确认一下这些step:

  1. 创建一个新的终端窗口
  2. 进入cluster-test目录
  3. 创建7006文件夹
  4. 在上一步基础之上,创建redis,conf文件,然后将端口改为7006.
  5. 使用命令”../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&gt; 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

注意,新节点现在已经链接上了集群,已经成为集群的组成部分,并且可以通过客户端的命令请求进行重定向了。但是,和其他master节点相比,新节点还有两个区别,如下:

新节点没有包含任何数据,因为其没有哈希槽
因为这个是一个没有哈希槽的master节点,所以,当一个slave节点想要成为master节点时,这个节点在推选策略中是不会被选中的。
接下来,只要使用redis-trib程序的重新分片功能,将某些哈希槽移动到这个新节点中,新节点就会成为一个真正的master节点。关于如何重新分片,我们上文已经进行了详细的介绍,其过程没有任何的区别。唯一的不同是将目标设置为这个7006空节点即可。

增加一个副本节点的方法


我们可以通过两个方法来实现增加一个副本节点。显而易见的其中一个方法就是使用redis-trib,但是其命令的带有“–slave”参数,具体如下:
./redis-trib.rb add-node --slave 127.0.0.1:7006 127.0.0.1:7000

注意:现在的这条命令和我们之前增加一个新节点的命令完全一样,因此,我们没有明确指明复制哪个master节点。在这种情况下:redis-trib将会为整个master节点中的任意一个增加其对应的新的包含少量复制的副本节点。

但是,你也可以明确的指定想要对其进行复制操作的目标master节点,具体命令如下:
./redis-trib.rb add-node --slave --master-id 3c3a0c74aae0b56170ccb03a76b60cfe7dc1912e 127.0.0.1:7006 127.0.0.1:7000

使用这种方法,我们就明确的分配了一个特定的master节点用来作为副本的目标对象。

另外一个方式:手动的为一个给定的master节点增加一个副本的方法是:先增加一个新节点作为空master节点,接着使用CLUSTER REPLICATE命令将其转变为一个副本。这条命令也可以用于将一个已经加入的slave节点变为另外一个master节点的副本时使用。
举个例子:为了7005节点增加一个副本,这个7005已经拥有11423号16383号哈希槽,其ID为3c3a0c74aae0b56170ccb03a76b60cfe7dc1912e,那么我们要做的就是将其链接到一个已经加入网络中的空节点,并且发送下面的命令:
redis 127.0.0.1:7006&gt; cluster replicate 3c3a0c74aae0b56170ccb03a76b60cfe7dc1912e

OK。现在我们就拥有了这些哈希槽的一个新副本,集群中其余所有的节点都已经更新了自己的节点列表及配置。我们可以通过下面的命令来确认上面的结果:

$ 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…有两个slave节点,运行在端口7002与7006上。


移除一个节点

为了移除一个节点,可以在redis-trib中使用下面的命令:
./redis-trib del-node 127.0.0.1:7000 `&lt;node-id&gt;`

第一个参数:集群中任意一个节点。

第二个参数:你想要移除的节点ID。
你也可以使用同样的方式移除一个master节点,但是前提是:这个master节点必须是空的。如果你要移除的master节点非空,你需要将其中的数据重新分片到其余的master节点上。另一种可选的方法是:手动的执行故障转移,当其变为一个新的master节点的slave节点是,就可以移除这个节点了。这种做法显然不能在想要减少集群中的master节点数量时使用,在这种情况下,就需要重新分片来实现了。

副本迁移

在Redis集群中,有可能将一个slave重复的配置来复制两个不同的master节点,可以使用下面的命令实现:
CLUSTER REPLICATE &lt;master-node-id&gt;

然而,这里存在着一个特殊情况:当你想从一个master节点自动的复制到另一个master节点,而不用系统管理员的协助。这种自动的对复制操作重配置方法被称为副本迁移,这种做法也能够提高Redis集群的可用性。

特别的:你可以在Redis集群规范文档中学习了解到副本迁移的详细内容,这里我们只提供关于主体思想的一些信息和你应该从中学习到的有用之处。
这里你可能想让你的集群在给定的条件下才从一个master节点复制到另一个节点的原因是:通常情况下Redis集群是作为对抗一定数量节点的失效就像给定master节点的副本一样。(这里翻译好乱,大牛请给指点一下)
举个例子:集群中的每一个master节点都拥有一个副本,当其中一个master节点及副本都同时失效之后,集群将不能够在提供服务。原因是:这里没有其他Redis实例拥有这个master节点所持有的哈希槽由此来提供服务。然而,当网络分裂发生时,非常有可能的同时孤立了一部分节点,其中一部分可能失效了(比如单个节点机器内的软件或者硬件故障等)。这是一个非常值得注意的失效类型,但其很少情况下会同时发生,因此,在你的集群中有可能发生的是:集群中某个master节点的slave节点在4点被kill,对应的master节点在6点被kill。这种情况仍然会导致集群不可用。
为了提高系统的可用性,我们可以为集群中的每一个master节点增加一个额外的副本,但是代价是高昂的。副本迁移允许只对一定数量的master节点增加更多的slave节点。因此,如果你拥有10个master节点,对应10个slave节点,共计20个Redis实例。然而,举个例子,你为其中的某些master节点增加了3个实例,因此实际上某个的master节点肯定会拥有超过单个slave的节点。
在副本迁移过程中,如果一个没有slave节点的master节点下线,一个拥有多个slave的master节点的副本将会迁移至这个孤立的master节点下。因此,在之前提及的例子中,slave节点在4点下线,另一个slave节点将会取代它的位置,然后当master节点在5点失效后,这里仍然拥有一个slave节点能够被选举为新的master节点,由此,集群也能够继续提供服务。
总结上面所说的内容,你需要在副本迁移中了解的内容是:
  • 在给定的时刻,集群将会尝试从一个拥有大量副本对象的master节点上迁移一个副本对象。
  • 为了从副本迁移中获得好处,你仅仅需要对集群中的一个单独的master节点增加少量的副本对象,至于是否为master节点无关紧要。
  • 这里有一个配置参数控制着副本迁移的功能:cluster-migration-barrier,你可以在由Redis集群提供的redis,conf文件中了解到更多内容。

Redis集群中的节点升级

升级一个slave节点是非常简单的,你仅仅需要停止这个节点,再用更新版本的Redis重新启动它。如果此时还有客户端在这个节点上进行都操作,那这些客户端有能力在当前节点不可用时,重写链接到另外一个slave节点上。
升级一个master节点相对复杂一点,建议的操作步骤如下:
  • 使用CLUSTER  FAILOVER 手动触发master节点的故障转移,目标对象为其slave节点。
  • 等待master转变为slave节点。
  • 最后,按照slave升级的步骤,再升级该节点。
  • 如果你想要刚刚升级的节点再次成为master节点,那么再手动触发一次故障转移操作,来达到这个目的。
在这个过程中,你应该在其他节点都已经升级完成之后再操作当前节点。

迁移到Redis集群

你可能想要将可能只有一个master节点的实例迁移为Redis集群,或者已经使用了已经存在的分片设置,其key已经被分配到N个节点当中。(使用了由客户端库实现或Redis代理的内部算法或者分片算法)
在上面的两种情况下,都是可以轻易的完成迁移到集群的操作,然而,最重要的细节是如果包含多个key的操作在被应用使用,因此,这里对应的有三种不同情况:
  • 多个key操作,或事务,或Lua脚本包含多个key,没有使用这些方法。Key的访问是独立的。(即使通过事务或Lua脚本将多个命令同时执行,对相同的key一起操作)
  • 多个key操作,或事务,或Lua脚本包含多个key,使用了这些方法,但仅仅在这些key有相同的hash tag,意思就是说,这些一起使用的key都有一个恰好相同的子字符串“{…}”。举个例子,下面的多个key操作就在相同的hash tag上下文中:SUNION {user:1000}.foo {user:1000}.bar
  • 多个key操作,或事务,或Lua脚本包含多个key,使用了这些方法,并且这些key的名称是不明确的,不相同的,或者hash tag也是不同的。
第三种情况是不被Redis所处理的:应用需要改变使用方法,为了不使用多个key操作,或者,只在相同hash tag的上下文中使用它们。
因此,前两种情况是能够被Redis处理的,因此,我们也重点关注这两种情况,并且这两种方式使用相同的处理方式,所以在本文中,我们也不对此做区分。
假设,你已经拥有了预先存在N个master节点数据,其中N等于1(如果你还没有预先存在的分片),接下来,为了将你的数据迁移到Redis集群中的步骤是十分必要的,如下:
  1. 停止你的客户端。当前非自动的实时的迁移到Redis集群是有可能的。你可以在你的应用/环境中进行计划性的动态迁移。
  2. 为你的N个master节点每一个都生成一个AOF文件,使用BGREWRITEAOF命令,然后等待AOF文件写入完成。
  3. 将你的aof-1到aof-n文件保存在某处。在这种情况下,你可以停止这些旧的Redis实例(这种做法是非常有用的,因为在非虚拟机部署的条件下,你经常需要重复使用相同的计算机)。
  4. 创建一个由N个master节点和0个slave节点构成Redis集群。待会再增加slave节点。确保你的所有节点都是用aof文件实现持久化。
  5. 停止刚刚创建的所有节点,再用之前的aof文件替换这些节点目录下面aof文件,如,aof-1,aof-2等等
  6. 重新启动Reids集群节点,加载这些AOF文件。这些节点重新编译这些aof文件中的key,而不是依照它们的配置。
  7. 使用reids-trib fix命令来修复集群,使得这些keys能够按照每个节点的哈希槽正确的迁移。每个节点有已经授权的,当然也有未授权的。
  8. 使用redis-trib check命令在末尾检查,确保你的集群正确的运行。
  9. 重新启动客户端,修改为使用Redis集群感知客户端的库。
还有另外一种可选的方式来讲数据从外部实例中导入Redis集群中:使用redis-trib的import命令。
这个命令将会将运行的实例中所有的key移动到给定的预先存在的Redis集群中(同时从源实例中删除该对象)。然而,请注意,如果使用2.8版本的Redis作为源实例,这个操作可能会非常慢,因为2.8版本中没有实现迁移链接的缓存,因此,你可能需要在执行这样的操作之前,使用Redis的3.0以上的版本来重新启动你的Redis示例。

————————————————————————————————————————————-
至此,NoSQL之Redis—集群教程
备注:
本文部分内容翻译的有些词不达意,建议各位看官最好对比官方文档学习,要是能够给点建议,那就再好不过了!
参考资料:
官方文档:http://redis.io/topics/cluster-tutorial
其他资料:http://doc.redisfans.com/topic/cluster-tutorial.html


未经允许不得转载:冰点网络 » NoSQL之Redis—集群教程

分享到:更多 ()

评论 抢沙发

评论前必须登录!