Redis Cluster
Redis Cluster 提供了一种 Redis 安装的方法,数据会在多个Redis节点之间自动分区。
Redis Cluster在分区期间还提供了一定程度的可用性。这实际上就是在某些节点出现故障或无法通信时继续操作的能力。但是,如果发生较大故障(例如,大多数节点不可用时),集群将停止运行。
Redis Cluster TCP 端口
每个Redis集群节点都需要打开两个TCP连接。一个用于服务客户端的常规Redis TCP端口,例如6379,另一个为在数据端口上加上10000所获得的端口,例如16379,用于集群总线,即使用二进制协议的节点到节点通信通道。
命令端口和集群总线端口偏移是固定的,并且始终为10000。
对于每个节点,要使Redis集群正常工作,您需要:
- 普通客户端通信端口(通常为6379)用于与客户端通信,以向需要访问集群的所有客户端以及所有其他集群节点开放。(使用客户端端口进行键的迁移)
- 集群总线端口(客户端端口+ 10000)必须可以从所有其他集群节点访问。
Redis Cluster 分区
Redis Cluster不使用一致性哈希算法,而是引入哈希槽的概念。
Redis集群中有 16384 个哈希槽,要计算给定 key 的哈希槽,我们只需对 key 的CRC16取模16384。
Redis集群中的每个节点都负责哈希槽的子集,因此,例如,您可能有一个包含3个节点的集群,其中:
- 节点A包含从0到5500的哈希槽。
- 节点B包含从5501到11000的哈希槽。
- 节点C包含从11001到16383的哈希槽。
这样可以轻松添加和删除集群中的节点。例如,如果我想添加一个新的节点D,则需要将一些哈希槽从节点A,B,C移到D。类似地,如果我想从集群中删除节点A,则只需移动A所服务的哈希槽到B和C。当节点A为空时,我可以将其从集群中完全删除。
Redis Cluster 主从模型
为了在主节点子集发生故障或无法与大多数节点通信时保持可用,Redis Cluster使用主从模型,其中每个哈希槽具有1~N个的从属节点作为副本。
在 A, B, C 的集群里,如果 A 节点出现问题,则0到5500的哈希槽将无法提供服务了。
在 A, A1, B, B1, C, C1 的集群中,A1 作为 A 的从属节点,节点 A1 复制 A,并且 A 失败,群集将把节点 A1 提升为新的主节点,并将继续正常运行。如果节点 A 和 A1 同时失败,则 Redis Cluster 无法继续运行。
从属节点的复制过程:
- 客户端往A节点写数据。
- A节点向客户端回复写入结果。
- A节点将写操作传播到其从机A1,A2和A3。
其中第三步是异步执行的。
Redis Cluster 一致性
Redis Cluster无法保证强一致性。这意味着在某些情况下,Redis Cluster可能会丢失系统已确认给客户端的写入。
Redis Cluster可能会丢失写入的第一个原因是因为它使用异步复制。在需要的情况下,可以使用 WAIT 命令来强制同步复制,使得丢失写入的可能性大大降低。即使使用同步复制,Redis Cluster也不实现强一致性:在更复杂的故障情况下,总是有可能将无法接收写入的从设备选为主服务器。
Redis集群配置参数
cluster-enabled<yes/no>
:当前节点是否启动redis cluster的支持。
cluster-config-file<filename>
:不是用户可编辑的配置文件,而是Redis Cluster节点每次发生更改时都会自动持久保存集群配置的文件。
cluster-node-timeout:如果主节点不可用的时间超过指定时间,则认为主节点已经故障,需要进行故障转移。
cluster-slave-validity-factor:如果子节点与主节点断开连接的时间超过 cluster-node-timeout * cluster-slave-validity-factor,则该子节点失去对主节点进行故障转移的资格。
cluster-migration-barrier:指定一个主节点最少应该拥有多少个子节点,未达到指定值,可能进行副本迁移。
cluster-require-full-coverage<yes/no>
:是否需要集群完整性,才能对外提供服务,如果是 no, 则只要不是所有的主节点都故障,正常的节点就还可以提供服务。
Redis 集群创建
一、逐个创建实例并集群
1. 创建实例目录 7000 ~ 7005 用来存放 6 个 redis 实例需要的配置文件。
zqj@zqj-legion:~/app/redis-cluster-instance$ ll
drwxrwxr-x 8 zqj zqj 4096 2月 27 10:48 ./
drwxrwxr-x 7 zqj zqj 4096 2月 27 10:52 ../
drwxrwxr-x 2 zqj zqj 4096 2月 27 10:58 7000/
drwxrwxr-x 2 zqj zqj 4096 2月 27 10:58 7001/
drwxrwxr-x 2 zqj zqj 4096 2月 27 10:58 7002/
drwxrwxr-x 2 zqj zqj 4096 2月 27 10:58 7003/
drwxrwxr-x 2 zqj zqj 4096 2月 27 10:58 7004/
drwxrwxr-x 2 zqj zqj 4096 2月 27 10:58 7005/
配置:
port 7000 #注意不同实例需要修改端口
cluster-enabled yes #开启集群功能
cluster-config-file nodes.conf
cluster-node-timeout 5000
appendonly yes
2. 进入实例目录,启动实例
zqj@zqj-legion:~/app/redis-cluster-instance$ cd ../7000
zqj@zqj-legion:~/app/redis-cluster-instance$ redis-server redis.conf
3. 创建集群
zqj@zqj-legion:~/app/redis/utils/create-cluster$ 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
>>> Performing hash slots allocation on 6 nodes...
Master[0] -> Slots 0 - 5460
Master[1] -> Slots 5461 - 10922
Master[2] -> Slots 10923 - 16383
Adding replica 127.0.0.1:7004 to 127.0.0.1:7000
Adding replica 127.0.0.1:7005 to 127.0.0.1:7001
Adding replica 127.0.0.1:7003 to 127.0.0.1:7002
>>> Trying to optimize slaves allocation for anti-affinity
[WARNING] Some slaves are in the same host as their master
M: 1c20e66acc44ce18c84fbe37edc98822402244b4 127.0.0.1:7000
slots:[0-5460] (5461 slots) master
M: 0c0ae536971019319245bb2cc5113af6f9d03115 127.0.0.1:7001
slots:[5461-10922] (5462 slots) master
M: e3af6eb131cf14fd6d88ca6472bc64bf5b147d77 127.0.0.1:7002
slots:[10923-16383] (5461 slots) master
S: dffbff434fc26f0495dc769c8cdd5be01d242743 127.0.0.1:7003
replicates 0c0ae536971019319245bb2cc5113af6f9d03115
S: 87ff20af26e1c9b608feef931b12ebac5d03006d 127.0.0.1:7004
replicates e3af6eb131cf14fd6d88ca6472bc64bf5b147d77
S: d103592b1ce1fb5f9b566911cc57511e96bfca94 127.0.0.1:7005
replicates 1c20e66acc44ce18c84fbe37edc98822402244b4
Can I set the above configuration? (type 'yes' to accept): yes
>>> Nodes configuration updated
>>> Assign a different config epoch to each node
>>> Sending CLUSTER MEET messages to join the cluster
Waiting for the cluster to join
..
>>> Performing Cluster Check (using node 127.0.0.1:7000)
M: 1c20e66acc44ce18c84fbe37edc98822402244b4 127.0.0.1:7000
slots:[0-5460] (5461 slots) master
1 additional replica(s)
S: 87ff20af26e1c9b608feef931b12ebac5d03006d 127.0.0.1:7004
slots: (0 slots) slave
replicates e3af6eb131cf14fd6d88ca6472bc64bf5b147d77
S: dffbff434fc26f0495dc769c8cdd5be01d242743 127.0.0.1:7003
slots: (0 slots) slave
replicates 0c0ae536971019319245bb2cc5113af6f9d03115
M: e3af6eb131cf14fd6d88ca6472bc64bf5b147d77 127.0.0.1:7002
slots:[10923-16383] (5461 slots) master
1 additional replica(s)
S: d103592b1ce1fb5f9b566911cc57511e96bfca94 127.0.0.1:7005
slots: (0 slots) slave
replicates 1c20e66acc44ce18c84fbe37edc98822402244b4
M: 0c0ae536971019319245bb2cc5113af6f9d03115 127.0.0.1:7001
slots:[5461-10922] (5462 slots) master
1 additional replica(s)
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
在集群模式下,要用redis-cli 连接集群节点,注意加上 -c ,否则会报错 (error) MOVED
redis-cli -c -p 7000
集群的其他功能
1. 集群可以重新用以下命令给某个节点多分配槽位。
redis-cli --cluster reshard <host>:<port> --cluster-from <node-id> --cluster-to <node-id> --cluster-slots <number of slots> --cluster-yes
2. 测试故障转移,使 7000 端口的redis 实例失败,则子节点会成为主节点对外提供服务,如果7004再失败,则整个集群不再继续服务。
zqj@zqj-legion:~/app/redis-cluster-instance$ redis-cli -p 7000 debug segfault
Error: Server closed the connection
zqj@zqj-legion:~/app/redis-cluster-instance$ redis-cli --cluster info 127.0.0.1:7001
Could not connect to Redis at 127.0.0.1:7000: Connection refused
*** WARNING: 127.0.0.1:7004 claims to be slave of unknown node ID 2de1338b7d0afa515d3db3363b17045c30269115.
127.0.0.1:7001 (375dac7d...) -> 0 keys | 5436 slots | 1 slaves.
127.0.0.1:7002 (de36e0e1...) -> 0 keys | 5437 slots | 1 slaves.
[OK] 0 keys in 2 masters.
0.00 keys per slot on average.
zqj@zqj-legion:~/app/redis-cluster-instance$ redis-cli --cluster info 127.0.0.1:7001
Could not connect to Redis at 127.0.0.1:7000: Connection refused
127.0.0.1:7001 (375dac7d...) -> 0 keys | 5436 slots | 1 slaves.
127.0.0.1:7004 (8d17187a...) -> 0 keys | 5511 slots | 0 slaves.
127.0.0.1:7002 (de36e0e1...) -> 0 keys | 5437 slots | 1 slaves.
[OK] 0 keys in 3 masters.
0.00 keys per slot on average.
3. 手动故障转移,Redis Cluster支持使用 CLUSTER FAILOVER 命令进行手动故障转移,该手动故障转移必须在要进行故障转移的主服务器的从服务器之一中执行。
4. 添加一个新节点作为主节点
# 在此之前需要创建一个新的 redis 实例 在7006端口
redis-cli --cluster add-node 127.0.0.1:7006 127.0.0.1:7000
其中第一个参数是新节点的地址+端口,第二个参数是任意一个已经存在于集群中可用的节点的地址+端口。
添加完之后该节点并不会自动分配到哈希槽位,因此它不保存任何数据,但是可以接受请求重定向到其他主节点服务。
因为它是没有分配插槽的主机,所以当从机要成为主机时,它不会参与选举过程。
M: 267ed19a55a05f51262cc560f1abce8603205a1b 127.0.0.1:7006
slots: (0 slots) master
这时可以用上面提到的 reshard 功能为该节点分配哈希槽。
5. 添加一个新节点作为副本
redis-cli --cluster add-node 127.0.0.1:7006 127.0.0.1:7000 --cluster-slave
redis-cli --cluster add-node 127.0.0.1:7006 127.0.0.1:7000 --cluster-slave --cluster-master-id 3c3a0c74aae0b56170ccb03a76b60cfe7dc1912e
添加一个副本节点,并且会被随机分配给某个主节点。也可以使用 --cluster-master-id 指定分配给哪个主节点。
还可以进入某个未分配哈希槽的主节点或者随便一个副本节点下,执行
cluster replicate <node-id>
就可以把当前节点分配给某个主节点作为副本。
6. 删除节点
redis-cli --cluster del-node 127.0.0.1:7000 `<node-id>`
第一个参数只是集群中的一个随机节点,第二个参数是您要删除的节点的ID。
可以用相同的方法删除主节点,但是要删除主节点,它必须为空。如果主节点不为空,则需要先将数据从其重新分片到所有其他主节点。
删除主节点的另一种方法是在其从节点之一上对其执行手动故障转移,并在该节点成为新主节点的从节点之后将其删除。
7. 副本迁移
在第5点提到的 cluster replicate <node-id> 命令,可以使当前节点成为目标节点的一个副本。
但是在没有人工干预的情况下,redis 集群也有可能会进行副本的重新配置,这称作副本迁移。能够提高Redis群集的可靠性。
例如,如果一个主节点及其副本同时失败,则每个主节点都只有一个副本的群集将无法继续操作,这仅仅是因为没有其他实例拥有该主节点服务的哈希槽的副本。
为了解决这种情况,我们可以为每个主节点都提供其他副本,但是代价相对比较昂贵。然而,我们还可以为少数几个主节点添加副本,这样就有几个主节点的副本数量大于1。当某个主节点已经没有可用的副本节点时,redis 会将具有多个副本节点的主节点的副本迁移到孤立的主节点上。
- 群集将尝试在给定时刻从具有最大副本数的主副本迁移副本。
- 要从副本迁移中受益,您只需将更多副本添加到群集中的单个主数据库中,哪个主数据库都没有关系。
- 有一个配置参数可控制称为“副本迁移” ,
cluster-migration-barrier 指定当主节点的副本少于多少时,开始副本迁移。
8. 升级Redis集群中的节点
一般是先对副本节点升级,然后使用手动故障转移 cluster failover 命令,将副本节点把未升级的主节点替换下来,然后升级。