七、Redis cluster
7.1 Redis集群
Redis 集群是一个可以在多个 Redis 节点之间进行数据共享的设施(installation)。
Redis 集群不支持那些需要同时处理多个键的 Redis 命令,因为执行这些命令需要在多个 Redis 节点之间移动数据,并且在高负载的情况下,这些命令将降低 Redis 集群的性能,并导致不可预测的行为。
Redis 集群通过分区(partition)来提供一定程度的可用性(availability):即使集群中有一部分节点失效或者无法进行通讯,集群也可以继续处理命令请求。将数据自动切分(split)到多个节点的能力。
当集群中的一部分节点失效或者无法进行通讯时,仍然可以继续处理命令请求的能力。
7.2 Redis 集群数据共享
Redis 集群使用数据分片(sharding)而非一致性哈希(consistency hashing)来实现:一个 Redis 集群包含 16384 个哈希槽(hash slot),数据库中的每个键都属于这 16384 个哈希槽的其中一个,集群使用公式CRC16(key) % 16384 来计算键 key 属于哪个槽,其中 CRC16(key) 语句用于计算键 key 的 CRC16 校验和 。
节点 A 负责处理 0 号至 5500 号哈希槽。
节点 B 负责处理 5501 号至 11000 号哈希槽。
节点 C 负责处理 11001 号至 16384 号哈希槽。
槽的计算公式
集群使用公式 CRC16(key) & 16383 计算键 key属于哪个槽。
7.3 集群运行机制
所有的redis节点彼此互联(PING-PONG机制),内部使用二进制协议优化传输速度和带宽。
节点的fail是通过集群中超过半数的master节点检测失效时才失效。
客户端与redis节点直连,不需要中间proxy层。客户端不需要连接集群所有节点,连接集群中任何一个可用节点即可
把所有的物理节点映射到[0-16383]slot上,cluster 负责维护node<->slot<->key
为了使得集群在一部分节点下线或者无法与集群的大多数(majority)节点进行通讯的情况下,仍然可以正常运作,Redis 集群对节点使用了主从复制功能:集群中的每个节点都有 1 个至 N 个复制品(replica),其中一个复制品为主节点(master),而其余的N-1 个复制品为从节点(slave)。
在之前列举的节点 A 、B 、C 的例子中,如果节点 B 下线了,那么集群将无法正常运行,因为集群找不到节点来处理5501号至11000号的哈希槽。
假如在创建集群的时候(或者至少在节点 B下线之前),我们为主节点B添加了从节点 B1,那么当主节点 B下线的时候,集群就会将B1设置为新的主节点,并让它代替下线的主节点B,继续处理5501号至11000号的哈希槽,这样集群就不会因为主节点B的下线而无法正常运作了。
不过如果节点B和B1都下线的话,Redis集群还是会停止运作。
集群的复制特性重用了 SLAVEOF 命令的代码,所以集群节点的复制行为和SLAVEOF 命令的复制行为完全相同。
7.4 集群的故障转移
-
在集群里面,节点会对其他节点进行下线检测。
-
当一个主节点下线时,集群里面的其他主节点负责对下线主节点进行故障移。
-
换句话说,集群的节点集成了下线检测和故障转移等类似 Sentinel 的功能。
-
因为 Sentinel 是一个独立运行的监控程序,而集群的下线检测和故障转移等功能是集成在节点里面的,它们的运行模式非常地不同,所以尽管这两者的功能很相似,但集群的实现没有重用 Sentinel 的代码。
在集群里面执行命令的两种情况
命令发送到了正确的节点:
命令要处理的键所在的槽正好是由接收命令的节点负责,那么该节点执行命令,就像单机 Redis 服务器一样。
槽位说明:
7000: 槽 0~5000
7001:槽 5001~10000
7002:槽 10001~16383
键 date 位于 2022 槽,该槽由节点 7000 负责,命令会直接执行。
命令发送到了错误的节点:
接收到命令的节点并非处理键所在槽的节点,那么节点将向客户端返回一个转向(redirection)错误,告知客户端应该到哪个节点去执行这个命令,客户端会根据错误提示的信息,重新向正确的节点发送命令。
键 date 位于 2022 槽,该槽由节点 7000 负责,但错误发送到了7001节点,7001向客户返回转向错误。
客户端根据转向错误的指引,转向到节点7000,并重新发送命令
7.5 关于转向错误
在集群中的节点会互相告知对方,自己负责处理哪些槽。
集群中的每个节点都会记录 16384 个槽分别由哪个节点负责,从而形成一个“槽表”(slot table)。
节点在接收到命令请求时,会通过槽表检查键所在的槽是否由本节点处理:
如果是的话,那么节点直接执行命令;
如果不是的话,那么节点就从槽表里面提取出正确节点的地址信息,然后返回转向错误。
7.6 配置集群
前期准备
- EPEL源安装ruby支持
yum install ruby rubygems -y
- 使用国内源
gem source -a http://mirrors.aliyun.com/rubygems/ -remove https://rubygems.org/
# gem sources --add https://gems.ruby-china.org/ --remove https://rubygems.org/
- 安装redis支持
gem install redis -v 3.3.3
gem sources -l
- 配置文件
Redis 集群由多个运行在集群模式(cluster mode)下的 Redis 实例组成,实例的集群模式需要通过配置来开启,开启集群模式的实例将可以使用集群特有的功能和命令。
以下是一个包含了最少选项的集群配置文件示例:
port 7000
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
appendonly yes
- 创建程序目录
cd /application/redis
mkdir 7000 7001 7002 7003 7004 7005
- 拷贝应用
for i in 0 1 2 3 4 5
do
cp /usr/local/redis/src/redis-server ./700$i
done
- 创建配置文件
for i in 7000 7001 7002 7003 7004 7005
do
echo "port $i
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
appendonly yes" > $i/redis.conf
done
- 启动redis集群
for i in 7000 7001 7002 7003 7004 7005
do
cd $i
./redis-server ./redis.conf &
cd ../
done
- 创建集群
cd /usr/local/redis/src/
./redis-trib.rb --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
给定 redis-trib.rb 程序的命令是 create,这表示我们希望创建一个新的集群。
选项 --replicas 1 表示我们希望为集群中的每个主节点创建一个从节点。
7.7 集群管理
- 写数据,查看集群状态
redis-cli -c -p 7000
set foo bar
get foo
- 重新分片实践
cd /usr/local/redis/src/
./redis-trib.rb reshard 127.0.0.1:7000
- 集群状态
redis-cli -p 7000 cluster nodes | grep master
- 故障转移
redis-cli -p 7002 debug segfault
- 查看状态
redis-cli -p 7000 cluster nodes | grep master
增加新的节点
./redis-trib.rb add-node 127.0.0.1:7006 127.0.0.1:7000
删除一个节点
redis-trib del-node ip:port '<node-id>'
删除master节点之前首先要使用reshard移除master的全部slot,然后再删除当前节点
添加一个从节点
./redis-trib.rb add-node --slave --master-id $[nodeid] 127.0.0.1:7008 127.0.0.1:7000
7.8 状态说明
集群最近一次向节点发送 PING 命令之后,过去了多长时间还没接到回复。
节点最近一次返回 PONG 回复的时间。
节点的配置节点(configuration epoch)
本节点的网络连接情况:例如 connected 。
节点目前包含的槽:例如 127.0.0.1:7001 目前包含号码为 5960 至 10921 的哈希槽。