Redis 集群没有使用一致性hash, 而是引入了哈希槽的概念.
Redis 集群有16384个哈希槽,每个key通过CRC16校验后对16384取模来决定放置哪个槽.集群的每个节点负责一部分hash槽,举个例子,比如当前集群有3个节点,那么:
节点 A 包含 0 到 5500号哈希槽.
节点 B 包含5501 到 11000 号哈希槽.
节点 C 包含11001 到 16384号哈希槽.
这种结构很容易添加或者删除节点. 比如如果我想新添加个节点D, 我需要从节点 A, B, C中得部分槽到D上. 如果我像移除节点A,需要将A中得槽移到B和C节点上,然后将没有任何槽的A节点从集群中移除即可.
由于从一个节点将哈希槽移动到另一个节点并不会停止服务,所以无论添加删除或者改变某个节点的哈希槽的数量都不会造成集群不可用的状态.
另外一种情况,某一个主节点失效,比如上面的A节点失效,会造成 0 到 5500号槽点不可用, 由于redis支持主从模型的支持,我们在创建A主节点的时候创建a1从节点,即便一个主节点A失效,他的a1也会立马升为新的主节点继续服务,从而使集群可继续服务。
集群的管理中,集群分片管理最为核心,是添加,移除集群的基础,我们优先介绍。
重新分片操作基本上就是将某些节点上的哈希槽移动到另外一些节点上面,重新分片并不会对正在运行的集群程序产生任何影响,可以在运行时分片。
我的集群目录结构:
查看主从节点的布局 CLUSTER NODES:
#进入redis client ,端口随意 redis-cli.exe -c -p 7003 #查看节点布局 cluster nodes #节点信息 7d1c4b578836ac19fc18fb3052f4a615d90f2bc2 127.0.0.1:7002 master - 0 1420428414526 0 connected 5494-10921 e5a0d9656dcac9519ea32e81dadefcb8b051831e 127.0.0.1:7004 slave 77d2016c8a40b46b81 f172f6f3eab4cef2ae16fb 0 1420428415526 0 connected 6c0eafd99a6a1153b6aa0f1a81209bf083495098 :0 myself,master - 0 0 0 connected 1095 6-16383 113e9b14bb52702cda940854ccbe56c7f0ea2cef 127.0.0.1:7007 master - 0 1420428412025 0 connected 0-32 5461-5493 10922-10955 b2524f2f3ebfac4da75fb415e1d0eb2fe514a96c 127.0.0.1:7006 slave 6c0eafd99a6a1153b6 aa0f1a81209bf083495098 0 1420428410525 0 connected 2b95f078f327523a5185b26239c2baeb8773613b 127.0.0.1:7005 slave 7d1c4b578836ac19fc 18fb3052f4a615d90f2bc2 0 1420428412525 0 connected 77d2016c8a40b46b81f172f6f3eab4cef2ae16fb 127.0.0.1:7001 master - 0 1420428413525 0 connected 33-5460
以上我们看到,主节点为: 7002,7007,7001,7003 子节点为:7004, 7006,7005。每个节点的槽区域,以及子节点的主节点是谁都可以展现。
cluster nodes 命令输出的含义:
1、节点ID 2、IP 3、端口 4、标志: master, slave, myself, fail, ... 5、如果是个从节点, 这里是它的主节点的NODE ID 6、集群最近一次向节点发送 PING 命令之后, 过去了多长时间还没接到回复。. 7、节点最近一次返回 PONG 回复的时间。 8、节点的配置纪元(configuration epoch):详细信息请参考 Redis 集群规范 。 9、本节点的网络连接情况:例如 connected 。 10、节点目前包含的槽:例如 127.0.0.1:7001 目前包含号码为 5960 至 10921 的哈希槽。
现在开始分片,我们尝试重新将10个槽重新分片,从节点7002分配到7001,注意:
1、必须知道目标节点的id
2、必须知道槽的来源,可以是all(其他每个master取一些),也可以是从其他节点借调(需要指定借调节点id)
3、目标和借调者,最好是主节点
分片操作(必须得有redis-trib.rb文件,没有的去源码src下找或者从上一篇文字找):
#ip和端口随意,只要存在服务即可 redis-trib.rb reshard 127.0.0.1:7001 ...一些检测信息... OK] All nodes agree about slots configuration. >> Check for open slots... >> Check slots coverage... OK] All 16384 slots covered. #上面检测成功,开始询问你 ow many slots do you want to move (from 1 to 16384)?
输出到上面这句的时候,就是问你将要移动(分配)多少个槽,此时只需要输入想要分配的数量即可,我们现在分配10个槽,输入10.
What is the receiving node ID?
询问你目标节点id,记得是ID哦,我们目标是7002,id为:7d1c4b578836ac19fc18fb3052f4a615d90f2bc2。输入。
Please enter all the source node IDs. Type 'all' to use all the nodes as source nodes for the hash slots. Type 'done' once you entered all the source nodes IDs. Source node #1:
此时询问你从哪里借调,有2个选择,输入all,此时系统会自动从其他节点借调,或者输入借调节点的id,从指定节点借调,我们从7001借调,输入节点id:77d2016c8a40b46b81f172f6f3eab4cef2ae16fb
Please enter all the source node IDs. Type 'all' to use all the nodes as source nodes for the hash slots. Type 'done' once you entered all the source nodes IDs. Source node #1:77d2016c8a40b46b81f172f6f3eab4cef2ae16fb Source node #2:
再次询问,输入done即可。
....一些移动检测信息... Do you want to proceed with the proposed reshard plan (yes/no)?
询问是否执行,输入yes
Do you want to proceed with the proposed reshard plan (yes/no)? yes Moving slot 33 from 127.0.0.1:7001 to 127.0.0.1:7002: Moving slot 34 from 127.0.0.1:7001 to 127.0.0.1:7002: Moving slot 35 from 127.0.0.1:7001 to 127.0.0.1:7002: Moving slot 36 from 127.0.0.1:7001 to 127.0.0.1:7002: Moving slot 37 from 127.0.0.1:7001 to 127.0.0.1:7002: Moving slot 38 from 127.0.0.1:7001 to 127.0.0.1:7002: Moving slot 39 from 127.0.0.1:7001 to 127.0.0.1:7002: Moving slot 40 from 127.0.0.1:7001 to 127.0.0.1:7002: Moving slot 41 from 127.0.0.1:7001 to 127.0.0.1:7002: Moving slot 42 from 127.0.0.1:7001 to 127.0.0.1:7002:
此时可以在执行下 clluster nodes 查看下节点的槽区域。
添加新的节点的基本过程就是添加一个空的节点然后移动一些数据给它,有两种情况,添加一个主节点和添加一个从节点(添加从节点时需要将这个新的节点设置为集群中某个节点的复制)
创建新的节点7008,配置和之前一样,只需把端口号改下,过程如下:
1、 进入cluster目录 2、创建7008文件夹 3、 拷贝其他端口所有文件,从redis.conf更改端口号为7008 4、 启动节点,redis-server.exe redis.conf
使用redis-trib 来添加这个节点到现有的集群中去.
#第一个参数新节点地址,第二个参数任意一个已存在的节点ip和端口 redis-trib.rb add-node 127.0.0.1:7008 127.0.0.7001 #添加成功 ....其他信息... [OK] All 16384 slots covered. Connecting to node 127.0.0.1:7008: OK >>> Send CLUSTER MEET to node 127.0.0.1:7008 to make it join the cluster. [OK] New node added correctly.
添加成功后可以使用 cluster nodes 来查看节点的信息。
以上可以看到7008已经连接行集群了,可以对客户端的命令进行转向了,但是此时的节点:
1、新节点没有包含任何数据, 因为它没有包含任何哈希槽. 2、尽管新节点没有包含任何哈希槽, 但它仍然是一个主节点, 所以在集群需要将某个从节点升级为新的主节点时, 这个新节点不会被选中。
此时节点还无法存储数据,想要能使用起来,需要使用上面重新分片的流程,将集群中其他一些数量的哈希槽移动到新节点,新节点就可以储存数据了。此处不再多说。
添加从节点有两种方式,不指定主节点和指定住节点。
不指定主节点
#slave表明是从节点,第一个是节点ip和端口,第二个随意存在就可以 redis-trib.rb add-node --slave 127.0.0.1:7008 127.0.0.1:7001
此处的命令和添加一个主节点命令类似,此处并没有指定添加的这个从节点的主节点,这种情况下系统会在其他的复制集中的主节点中随机选取一个作为这个从节点的主节点。
指定主节点
#master-id 主节点id, 从节点ip和端口 ,主节点ip和端口 redis-trib.rb add-node --slave --master-id 77d2016c8a40b46b81f172f6f3eab4cef2ae16fb 127.0.0.1:7008 127.0.0.1:7001
以上是给主节点 127.0.0.1:7001添加一个从节点127.0.0.1:7001,主节点是:77d2016c8a40b46b81f172f6f3eab4cef2ae16fb
改变从节点的主节点
命令:redis-tribdel-node 任意节点ip:port 移除的节点id
比如我们要移除节点7008
redis-trib del-node 127.0.0.1:7001 b29720bfc1a52df3ee8e101eb5b07e30a09af965 >>> Removing node b29720bfc1a52df3ee8e101eb5b07e30a09af965 from cluster 127.0.0. 1:7001 Connecting to node 127.0.0.1:7001: OK Connecting to node 127.0.0.1:7007: OK Connecting to node 127.0.0.1:7005: OK Connecting to node 127.0.0.1:7006: OK Connecting to node 127.0.0.1:7002: OK Connecting to node 127.0.0.1:7008: OK Connecting to node 127.0.0.1:7003: OK Connecting to node 127.0.0.1:7004: OK >>> Sending CLUSTER FORGET messages to the cluster... >>> SHUTDOWN the node.
移除成功,需要注意的是,需要确保这个主节点是空的. 如果不是空的,需要将这个节点的数据重新分片到其他主节点上,具体的移除该节点的槽,根据重新分片流程来即可。
改变一个从节点的主节点的情况,命令: cluster replicate 主节点id
#当前节点7008 的主节点改变为 7001, 命令后面是主节点id redis 127.0.0.1:7008> cluster replicate 77d2016c8a40b46b81f172f6f3eab4cef2ae16fb
以上就是改变7008的归属主节点。
一般情况下,某一个主节点产生了故障了,系统会自动进行故障转移,把他的从节点自动升级为主节点。由于redis使用的是异步复制,所以在执行故障转移期间,集群会丢失某些写的命令。
或者我们在主节点没有任何问题的情况下强制手动故障转移也是很有必要的,比如想要升级主节点的Redis进程,我们可以通过故障转移将其转为slave再进行升级操作来避免对集群的可用性造成很大的影响。
Redis集群使用 CLUSTER FAILOVER
命令来进行故障转移,不过要被转移的主节点的从节点上执行该命令
手动故障转移比主节点失败自动故障转移更加安全,因为手动故障转移时客户端的切换是在确保新的主节点完全复制了失败的旧的主节点数据的前提下下发生的,所以避免了数据的丢失。
手动故障转移,命令:
测试阶段,主要是下载一个redis-rb-cluster ,这个是ruby实现的,对redis-rb的简单包装,解压后更改下example_test.rb的配置,我们可以看到如下输出:
redis-rb-cluster-master>example_test.rb 1 2 3 4 5 6 7 8 9 10 11 12
这个就是不断的向集群里面写入foo和读取foo,主要是用来测试集群的写入的.
(此处转的)redis-rb-cluster 项目包含了一个名为 consistency-test.rb 的示例应用, 这个应用比起 example.rb 有趣得多: 它创建了多个计数器(默认为 1000 个), 并通过发送 INCR 命令来增加这些计数器的值。
在增加计数器值的同时, consistency-test.rb 还执行以下操作:
每次使用 INCR 命令更新一个计数器时, 应用会记录下计数器执行 INCR 命令之后应该有的值。 举个例子, 如果计数器的起始值为 0 , 而这次是程序第 50 次向它发送 INCR 命令, 那么计数器的值应该是 50 。
在每次发送 INCR 命令之前, 程序会随机从集群中读取一个计数器的值, 并将它与自己记录的值进行对比, 看两个值是否相同。
换句话说, 这个程序是一个一致性检查器(consistency checker): 如果集群在执行 INCR 命令的过程中, 丢失了某条 INCR 命令, 又或者多执行了某条客户端没有确认到的 INCR 命令, 那么检查器将察觉到这一点 —— 在前一种情况中, consistency-test.rb 记录的计数器值将比集群记录的计数器值要大; 而在后一种情况中, consistency-test.rb 记录的计数器值将比集群记录的计数器值要小。
运行 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) |