·Redis集群提供了一种运行Redis安装的方法,在该安装中,数据会在多个Redis节点之间自动分片。css
Redis集群在分区期间还提供了必定程度的可用性,这其实是在某些节点出现故障或没法通讯时有继续工做的能力。可是,若是发生较严重故障(例如,大多数主节点不可用时),集群将中止运行。node
实际上,Redis集群能给你带来什么?git
每一个Redis集群节点都须要打开两个TCP链接。用于服务客户端的常规RedisTCP端口,例如6379
,再加上将数据端口加10000
的端口,好比在示例中为16379
。github
第二个值更大一点的端口用于集群总线,也就是使用二进制协议的节点到节点之间的通讯通道。节点将集群总线用于故障检测,配置更新,故障转移受权等。客户端永远不要尝试与集群总线端口进行通讯,而应始终与普通的Redis命令端口进行通讯,可是请确保您在防火墙中同时打开了这两个端口,不然Redis集群节点将没法进行通讯。redis
命令端口和集群总线端口的偏移量是固定的,而且始终为10000
。数据库
请注意,对于每一个节点,要使Redis集群正常工做,您须要:swift
若是您没有同时打开两个TCP端口,则集群将没法正常工做。promise
集群总线使用不一样的二进制协议进行节点到节点的数据交换,它更适合于在节点之间使用较少的带宽和较少的处理时间来交换信息。缓存
Redis集群不使用一致性哈希,而是使用一种不一样形式的分片,从概念上讲每一个key都是咱们称为hash槽的一部分。安全
Redis集群中有16384个hash槽,要计算给定key的hash槽,需将key的CRC16值用16384取模。
Redis集群中的每一个节点都负责hash槽的子集,例如,您可能有一个包含3个节点的集群,其中:
这样能够轻松添加和删除集群中的节点。例如,若是我想添加一个新节点D,则须要将一些hash槽从节点A,B,C移到D。相似地,若是我想从集群中删除节点A,则只需移动A所服务的hash槽到B和C。当节点A为空时,我能够将其从集群中彻底删除。
由于将hash槽从一个节点移动到另外一个节点不须要中止操做,因此添加删除节点或更改节点服务的hash槽的百分比不须要停机。
只要单个命令执行(或整个事务或Lua脚本执行)中涉及的全部key都属于同一个hash槽,Redis集群就支持多key操做。用户能够经过使用称为hash标签的概念来强制多个key成为同一hash槽的一部分。
hash标签记录在Redis集群规范中,注意,若是key的{}中的括号之间有一个子字符串,则仅对字符串中的内容进行hash处理,例如,一个叫{foo}的key和另外一个叫{foo} 的 key保证在同一hash槽中,而且能够在以多个key做为参数的命令中一块儿使用。
为了在主节点子集发生故障或没法与大多数节点通讯时保持可用,Redis集群使用主备模型,其中每一个hash槽具备从1(主节点自己)到N个副本(N -1个其余备份节点)。
在一个包含节点A,B,C的集群中,若是节点B失败,则集群将没法继续,由于咱们不能为 5501-11000 范围内的hash槽提供服务。可是,在建立集群(或稍后)时,咱们向每一个主节点添加一个备份节点,以便最终集群由做为主节点的A,B,C和做为备份节点的A1,B1,C1组成 ,若是节点B发生故障,系统将可以持续运行。节点B1复制B,而且B发生故障,集群会将节点B1提高为新的主节点,并将继续正常运行。
可是请注意,若是节点B和B1同时失败,则Redis集群没法继续运行。
Redis集群没法保证强一致性。 实际上,这意味着在某些状况下,Redis集群可能会丢失系统给客户端的已经确认的写操做。
Redis集群可能丢失写入的第一个缘由是由于它使用异步复制。这意味着在写入期
间会发生如下状况:
如您所见,B在回复客户端以前不会等待B1,B2,B3的确认,由于这会对Redis形成延迟,所以,若是您的客户端进行了写操做,而后B会确认,可是在它把写操做发送给备份节点以前崩溃了,此时其中一个备份节点(未接收到写操做)能够升级为主节点,这样就永远丢失该写操做。这与配置为每秒将数据刷新到磁盘的大多数数据库所发生的状况很是类似,所以因为过去使用不涉及分布式系统的传统数据库系统的经验,您已经能够对此进行合理推断。一样,您能够经过强制数据库在答复客户端以前刷新磁盘上的数据来提升一致性,但这一般会致使性能太低。在Redis集群下,这至关于同步复制。
基本上,在性能和一致性之间进行权衡是必须的。
Redis集群在须要时能够经过WAIT命令实现同步写,这使得丢失写的可能性大大下降,可是请注意,即便使用同步复制,Redis集群也不实现强一致性:在更复杂的状况下,老是有可能存在一种场景,就是一个没法接收数据的备份节点被选为主节点。
还有一种值得注意的状况,Redis集群也会丢失写操做,这种状况发生在网络分区期间,在该分区中,客户端与少数实例(至少包括主节点)隔离。
以咱们的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.conf文件中引入的配置参数。
继续阅读下去您就会得到更多清晰的要点。
注意:手动部署Redis集群,了解其某些操做很是重要。 可是,若是要尽快创建集群并运行,请跳过本节和下一节,直接转到使用 create-cluster 脚本建立Redis集群。
要建立集群,咱们须要作的第一件事就是让一些空Redis实例运行在集群模式下。基本上,这意味着不能使用常规Redis实例来建立集群,由于须要配置特殊模式,以便Redis实例启用集群特定的功能和命令。
如下是最小的Redis集群配置文件:
port 7000 # 端口号 cluster-enabled yes # 是成为cluster节点 cluster-config-file nodes.conf # 节点配置文件 cluster-node-timeout 5000 # 节点超时时间 appendonly yes # 是否使用aof
启用集群模式的只须要直接打开cluster-enabled命令。每一个实例还包含该节点配置存储位置的文件路径,默认状况下为nodes.conf。 该文件不会被人接触。 它只是由Redis集群实例在启动时生成,并在须要时进行更新。
请注意,按预期工做的最小集群要求至少包含三个主节点。 对于您的第一个测试,强烈建议启动一个包含三个主节点和三个备份节点的六个节点集群。为此,输入一个新目录并建立如下目录,该目录以咱们将在给定目录中运行的实例的端口号命名。就像是:
mkdir cluster-test cd cluster-test mkdir 7000 7001 7002 7003 7004 7005
在从7000到7005的每一个目录中建立一个redis.conf文件。做为配置文件的模板,只需使用上面的小示例,但请确保根据目录名称用正确的端口号替换端口号7000。如今,将您的redis-server可执行文件(从GitHub不稳定分支中的最新资源编译而来)复制到cluster-test目录中,最后在您喜欢的终端应用程序中打开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集群命令行实用程序,咱们可使用它来建立新集群,检查或从新分片现有集群等。
对于Redis版本3或4,有一个称为redis-trib.rb的较老的工具,它很是类似。您能够在Redis源代码分发的src目录中找到它。 您须要安装redis gem才能运行redis-trib。
gem install redis
第一个示例,即集群建立,将在Redis 5中使用redis-cli以及在Redis 3和4中使用redis-trib来显示。可是,接下来的全部示例都将仅使用redis-cli,由于您能够看到他们语法很是类似,您也可使用redis-trib.rb help来获取有关语法的信息,从而将一个命令行简单地更改成另外一命令行。 重要:请注意,若是须要,能够对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
对于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
此处使用的命令是create,由于咱们要建立一个新集群。 选项
--cluster-replicas 1表示咱们但愿每一个建立的主节点都具备一个备份节点。其余参数是要用于建立新集群的实例
的地址列表。
显然,知足咱们要求的惟一设置是建立具备3个主节点和3个从节点的集群。
Redis-cli将为您提供配置。 键入yes,将接受建议的配置。集群将被配置并加入,这意味着实例将被启动而后彼此
之间能够对话。 最后,若是一切顺利,您将看到以下消息:
[OK] All 16384 slots covered
意思就是说至少有一个主节点实例服务于16384个槽位中某一个.
若是您不想如上所述经过手动配置和执行单个实例来建立Redis集群,则可使用更简单的系统(可是您将不会学到一样多的操做细节)。
只需检查Redis发行版中的utils / create-cluster目录。内部有一个名为create-cluster的脚本(名称与包含在其中的目录相同),它是一个简单的bash脚本。 为了启动具备3个主节点和3个备份节点的6节点集群,只需键入如下命令:
在步骤2中,当redis-cli但愿您接受集群布局时,回复yes。
如今,您能够与集群进行交互,默认状况下,第一个节点将从端口30001开始。 完成后,使用如下命令中止集群:
create-cluster stop.
关于如何运行这个脚本的更多信息,请阅读目录里的README。
到目前为止,Redis集群的问题之一是缺乏客户端库的实现。
据我所知有如下实现:
测试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:7000> get hello -> Redirected to slot [866] located at 127.0.0.1:7000 "world"
注意:若是使用脚本建立集群,则节点可能会侦听不一样的端口,默认状况下从30001开始。
redis-cli的支持很是基础,所以它始终基于如下事实:Redis集群节点可以将客户端重定向到正确的节点。一个严格的客户端能够作得更好,而且能够在hash槽和节点地址之间缓存映射,以便直接使用与节点的正确链接。仅在集群配置中发生某些更改时(例如,在故障转移以后或系统管理员经过添加或删除节点来更改集群布局以后),才会刷新映射。
在继续展现如何操做Redis集群以前,好比执行故障转移或从新分片之类的操做,咱们须要建立一些示例应用程序,或者至少要可以理解简单的Redis集群客户端交互的语义。
经过这种方式,咱们能够运行一个示例,同时尝试使节点发生故障或开始从新分片,以了解Redis集群在现实环境下的行为。只是观察一个没有写入任何数据的集群是没有帮助的。
本节说明了redis-rb-cluster的一些基本用法,其中显示了两个示例。 首先是如下内容,它是redis-rb-cluster发行版中的example.rb文件:
1 require './cluster' 2 3 if ARGV.length != 2 4 startup_nodes = [ 5 {:host => "127.0.0.1", :port => 7000}, 6 {:host => "127.0.0.1", :port => 7001} 7 ] 8 else 9 startup_nodes = [ 10 {:host => ARGV[0], :port => ARGV[1].to_i} 11 ] 12 end 13 14 rc = RedisCluster.new(startup_nodes,32,:timeout => 0.1) 15 16 last = false 17 18 while not last 19 begin 20 last = rc.get("__last__") 21 last = 0 if !last 22 rescue => e 23 puts "error #{e.to_s}" 24 sleep 1 25 end 26 end 27 28 ((last.to_i+1)..1000000000).each{|x| 29 begin 30 rc.set("foo#{x}",x) 31 puts rc.get("foo#{x}") 32 rc.set("__last__",x) 33 rescue => e 34 puts "error #{e.to_s}" 35 end 36 sleep 0.1 37 }
该程序作了一件很是简单的事情,它将foo
所以,若是您运行该程序,其结果将和如下命令是同样的效果:
该程序看起来比较复杂,由于它须要在屏幕上显示错误而不是异常退出,所以,对集群执行的每一个操做都应该由错误处理包装。
第14行是程序中的第一个有趣的行。它建立Redis集群对象,使用启动节点列表做为参数,并容许该对象与不一样节点创建的最大链接数,最后是超时时间,对于给定的操做多少时间后被视为失败。
启动节点不须要是集群的全部节点。但至少有一个节点是可达的。还要注意,只要可以与第一个节点链接,redis-rb-cluster就会更新此启动节点列表。您应该指望任何其余严格的客户端都应该采起这种行为。
如今咱们已经将Redis集群对象实例存储在rc变量中,咱们能够像使用普通的Redis对象实例同样使用该对象了。
这刚好发生在第18至26行中:从新启动示例时,咱们不想以foo0从新开始,所以咱们将计数器存储在Redis自己内。上面的代码旨在读取此计数器,或者若是不存在该计数器,则为其分配零值。
可是请注意这是一个while循环,由于即便集群关闭并返回错误,咱们也要一次又一次尝试。普通的应用程序不须要那么当心。
28和37之间开始主循环,在该循环中设置key或显示错误。
注意循环结束时的sleep调用。在测试中,若是您想尽量快地写入集群,则能够删除sleep(相对来讲,这只是一个很繁忙的循环操做,它并无真正的并行,所以,在最好的条件下,您一般将得到每秒10k个操做))。
一般,为了使示例程序更容易被人看懂,写入速度会减慢。启动应用程序将产生如下输出:
ruby ./example.rb 1 2 3 4 5 6 7 8 9 ^C (I stopped the program here)
这不是一个很是有趣的程序,咱们稍后将使用更好的程序,可是咱们已经能够看到程序运行时,在从新分片期间都发生了什么。
如今,咱们准备尝试集群从新分片。 为此,请保持example.rb程序运行,以便您查看对程序的运行是否有影响。另外,您可能想注释一下sleep调用,以便在从新分片期间发生一些更严重的写入负载。从新分片基本上意味着将hash槽从一组节点移动到另外一组节点,而且像集群建立同样,它使用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个hash槽,若是该示例仍在运行且没有sleep调用,则该hash槽应已包含少许的key。
而后redis-cli须要知道从新分片的目标是什么,也就是将接收hash槽的节点。 我将使用第一个主节点,即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
因此个人目标节点应该是是 97a3a64667477371c4479320d683e4c8db5858b1。
如今,它会问你要从哪些节点获取这些key。我只输入all,以便从全部其余主节点获取一些hash槽。
最终确认后,您会看到一条消息,代表redis-cli将要从一个节点移动到另外一个节点,而且将从一侧移动到另外一侧的每一个实际的key都会打印出来。
在从新分片过程当中,您应该可以看到示例程序运行不受影响。若是须要,您还能够在从新分片期间中止并从新启动它屡次。从新分片结束时,可使用如下命令测试集群的运行情况:
redis-cli --cluster check 127.0.0.1:7000
全部插槽都会被覆盖到,可是此次127.0.0.1:7000的主节点将具备更多的hash插槽
,大约为6461。
咱们以前编写的示例程序不怎么好。它以一种简单的方式写入集群,甚至无需检查写入的内容是否正确。从咱们的角度来看,接收写操做的集群能够始终在每一个操做里将名为foo的key写到42这个hash槽里,而咱们根本不会注意到。所以,在redis-rb-cluster代码仓库中,有一个更有趣的程序,称为consistency-test.rb。它使用一组计数器,默认为1000,而且发送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) |
该行显示执行的读取和写入的次数,以及错误的数目(因为系统不可用,所以因为错误而没法接受查询)。若是发现不一致,则将新行添加到输出中。例如,若是我在程序运行时手动重置了计数器,就会发生这种状况:
$ 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 集群故障转移。
注意:在此测试过程当中,你应打开一个tab标签页并在上面运行一致性测试应用程序。
为了触发故障转移,咱们能够作的最简单的事情(也就是在分布式系统中可能发生的语义上最简单的失败)是使单个进程崩溃,在咱们的例子中是单个主机崩溃。
咱们可使用如下命令来识别主节点并使其崩溃:
$ 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
如今咱们能够看看这个一致性测试的输出的报告是什么。
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命令的输出可能看起来很复杂,但实际上很是简单,由如下标记组成:
有时,强制进行故障转移而实际上不会对主节点引发任何问题是颇有用的。例如,为了升级主节点之一的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中启动一个新节点(现有的6个节点已经从7000到7005使用新节点)同样简单,除了端口号以外,其余节点都使用相同的配置,所以您应该按顺序进行操做以符合咱们以前节点使用的设置:
此时这个服务应该运行起来了。如今咱们可使用redis-cli来向已有的集群添加一个节点。
redis-cli --cluster add-node 127.0.0.1:7006 127.0.0.1:7000
如您所见,我使用add-node命令将新节点的地址指定为第一个参数,并将集群中随机存在的节点的地址指定为第二个参数。实际上,redis-cli在这里对咱们没什么用,它只是向节点发送了CLUSTERMEET消息,这也能够手动完成。不过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的从新分片功能将hash槽分配给该节点。像上一节中已经展现的那样,这里我就不展现了,他们的操做没有区别,只是将空节点做为目标进行从新分片。
添加一个备份节点能够经过2种方式完成。最经常使用的是用 redis-cli, 不过要用--cluster-slave
选项,就像这样:
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添加副本,此节点当前服务的hash槽正在11423-16383范围内,节点ID为3c3a0c74aae0b56170ccb03a76b60cfe7dc1912e,我要作的就是链接到新节点(已经做为空的主节点添加到集群)并在新节点上发送命令:
redis 127.0.0.1:7006> cluster replicate 3c3a0c74aae0b56170ccb03a76b60cfe7dc1912e
就是这样。 如今,这组hash槽有了一个新副本,而且集群中的全部其余节点都已经知道(几秒钟后须要更新其配置)。 咱们可使用如下命令进行验证:
$ 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...如今有了2个备份节点,运行在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集群一般具备与给定主节点的副本数量相同的故障容忍性。
例如,若是一个主节点及其副本同时失败,则每一个主节点都有一个副本的集群将没法继续工做,这仅仅是由于没有其余实例拥有与该主节点服务的相同的hash槽的副本。可是,尽管网络断裂可能会同时隔离多个节点,可是许多其余类型的故障(例如单个节点本地的硬件或软件故障)是很是值得注意的一类故障,这类故障不太可能同时发生,所以在每一个主节点都有一个备份节点的集群中,若是该备份节点在凌晨4点被关闭,而主节点在凌晨6点被关闭。这仍然会致使集群没法运行。
为了提升系统的可靠性,咱们能够选择向每一个主节点添加副本,但这成本很高。副本迁移容许将更多备份节点添加到少数几个主节点中。所以,您有10个节点,每一个节点有1个备份节点,总共20个实例。可是,好比您增长了3个实例做为某些主节点的备份节点,所以某些主节点将具备多个副本了。
使用副本迁移时,若是一个主节点不包含备份节点,则具备多个备份节点的主节点的副本将迁移到孤立的主节点。所以,当您的备份节点在上述示例中的凌晨4点关闭以后,另外一个备份节点将接替它;当主节点在凌晨5点也发生故障时,另外一个备份节点将被选举成为主节点,以便集群能够继续操做。
因此您应该了解哪些有关副本迁移的知识?