一、背景介绍

我们先来一个简单的计算: 一天24个小时,用户真正有效访问时长暂且定为16个小时,那么就是 3亿次÷16小时÷3600秒=5208.3次/秒5208.3次/秒在每天面对海量数据和高并发场景的背后,一个可靠的系统架构显得尤为重要。常见的高可用、高并发、高性能处理方法比如有LVS、keepalived、heartbeat、haproxy集群,CDN加速,动态语言静态化,cache缓存,数据库的缓存优化等等。今天我们不聊架构,就单纯聊聊其中缓存层,谈谈如何部署一个高性能,多节点的redis集群。
(扫描下面二维码,快速查快递、寄快递!)

二、基本简述

2.1、什么是redis?

简单的一句话就是Redis是一款功能非常强大的NoSql数据库,是基于内存存储的数据库软件,在实际开发中经常被用作缓存,临时存储数据。使用Redis主要是为了分担数据库的压力和临时存储一些供不同进程可以共享使用的数据。

2.2 解决了那些问题?

redis最常见的用途就是作为缓存使用。如果客户端每次直接请求数据库,数据库显然无法承受这个压力,随时都有可能宕机。

一个常见的执行流程是:
  1. 1、用户发起请求;
  2. 2nginx通过反向代理将请求转发至tomcat;
  3. 3tomcatredis查询数据,如果redis存在请求需要的数据则返回,如果redis中不存在,则去数据库查询;
  4. 4、从数据库中查询到的数据返回给tomcat的同时也将查到的数据放入redis,供下次访问使用;

Redis  部署--[扩展]实战:亿级Redis集群解决方案 - 图1

2.3、常见部署方式优缺点

单节点redis
优点:结构简单,部署方便;
缺点: 节点单一,不保证数据的可靠性;

主从模式
优点: 双机主备架构,在一定程度上保证数据的完整性;
缺点: 故障恢复复杂,必要时需要人为干预;

集群模式
优点: 无中心架构,高扩展性,高可用性;
缺点:部署过程相对复杂,需要更多的节点资源;

经过研究比较, 确认由官方推出的Redis Cluster分布式集群解决方案具有高扩展性,高可用性,高性能,满足业务场景需要,因此选定Redis Cluster集群方案(当然也还有其他的集群方案,这里暂不讨论),下面就聊聊redis cluster集群方案。

三、浅述Redis Cluster原理

为什么要做Redis Cluster集群?

1、主从复制不能实现高可用;
2、随着业务的发展,用户数量增多,并发越来越多,业务需要更高的QPS,而主从复制中单机的QPS可能无法满足业务需求;
3、数据量的考虑,现有服务器内存不能满足业务数据的需要时,单纯向服务器添加内存不能达到要求,此时需要考虑分布式需求,把数据分布到不同服务器上;
4、网络流量需求:业务的流量已经超过服务器的网卡的上限值,可以考虑使用分布式来进行分流;
5、离线计算,需要中间环节缓冲等别的需求;

3.1 为什么要做数据分布?

全量数据,单机Redis节点无法满足要求,按照分区规则把数据分到若干个子集当中。
Redis  部署--[扩展]实战:亿级Redis集群解决方案 - 图2

3.2 常用数据分布方式之顺序分布

比如:1到100个数字,要保存在3个节点上,按照顺序分区,把数据平均分配三个节点上,1号到33号数据保存到节点1上,34号到66号数据保存到节点2上,67号到100号数据保存到节点3上
Redis  部署--[扩展]实战:亿级Redis集群解决方案 - 图3

3.3 常用数据分布方式之哈希分布

例如:1到100个数字,对每个数字进行哈希运算,然后对每个数的哈希结果除以节点数进行取余,余数为1则保存在第1个节点上,余数为2则保存在第2个节点上,余数为0则保存在第3个节点,这样可以保证数据被打散,同时保证数据分布的比较均匀。
哈希分布方式分为三个分区方式,下面介绍下每个分区方式基本原理:

3.3.1 节点取余分区:

比如有100个数据,对每个数据进行hash运算之后,与节点数进行取余运算,根据余数不同保存在不同的节点上。
Redis  部署--[扩展]实战:亿级Redis集群解决方案 - 图4

节点取余方式是非常简单的一种分区方式。

节点取余分区方式有一个问题:即当增加或减少节点时,原来节点中的80%的数据会进行迁移操作,对所有数据重新进行分布。

节点取余分区方式建议使用多倍扩容的方式,例如以前用3个节点保存数据,扩容为比以前多一倍的节点即6个节点来保存数据,这样只需要适移50%的数据。数据迁移之后,第一次无法从缓存中读取数据,必须先从数据库中读取数据,然后回写到缓存中,然后才能从缓存中读取迁移之后的数据。

Redis  部署--[扩展]实战:亿级Redis集群解决方案 - 图5

节点取余方式优点:
1、客户端分片;
2、配置简单:对数据进行哈希,然后取余。

节点取余方式缺点:
数据节点伸缩时,导致数据迁移,迁移数量和添加节点数据有关,建议翻倍扩容。

3.3.2 一致性哈希分区

一致性哈希原理:

将所有的数据当做一个token环,token环中的数据范围是0到2的32次方。然后为每一个数据节点分配一个token范围值,这个节点就负责保存这个范围内的数据。
Redis  部署--[扩展]实战:亿级Redis集群解决方案 - 图6

对每一个key进行hash运算,被哈希后的结果在哪个token的范围内,则按顺时针去找最近的节点,这个key将会被保存在这个节点上。
Redis  部署--[扩展]实战:亿级Redis集群解决方案 - 图7

在上面的图中,有4个key被hash之后的值在在n1节点和n2节点之间,按照顺时针规则,这4个key都会被保存在n2节点上,如果在n1节点和n2节点之间添加n5节点,当下次有key被hash之后的值在n1节点和n5节点之间,这些key就会被保存在n5节点上面了。

在上面的例子里,添加n5节点之后,数据迁移会在n1节点和n2节点之间进行,n3节点和n4节点不受影响,数据迁移范围被缩小很多。同理,如果有1000个节点,此时添加一个节点,受影响的节点范围最多只有千分之2,一致性哈希一般用在节点比较多的时候。

一致性哈希分区优点:
采用客户端分片方式:哈希 + 顺时针(优化取余),节点伸缩时,只影响邻近节点,但是还是有数据迁移。

一致性哈希分区缺点:
翻倍伸缩,保证最小迁移数据和负载均衡。

3.3.3 虚拟槽分区

虚拟槽分区是Redis Cluster采用的分区方式
预设虚拟槽,每个槽就相当于一个数字,有一定范围。每个槽映射一个数据子集,一般比节点数大

Redis Cluster中预设虚拟槽的范围为0到16383

Redis  部署--[扩展]实战:亿级Redis集群解决方案 - 图8

步骤:

1、把16384槽按照节点数量进行平均分配,由节点进行管理
2、对每个key按照CRC16规则进行hash运算
3、把hash结果对16383进行取余
4、把余数发送给Redis节点
5、节点接收到数据,验证是否在自己管理的槽编号的范围,如果在自己管理的槽编号范围内,则把数据保存到数据槽中,然后返回执行结果;如果在自己管理的槽编号范围外,则会把数据发送给正确的节点,由正确的节点来把数据保存在对应的槽中。

需要注意的是:Redis Cluster的节点之间会共享消息,每个节点都会知道是哪个节点负责哪个范围内的数据槽。

虚拟槽分布方式中,由于每个节点管理一部分数据槽,数据保存到数据槽中。当节点扩容或者缩容时,对数据槽进行重新分配迁移即可,数据不会丢失。

虚拟槽分区特点:
使用服务端管理节点,槽,数据:例如Redis Cluster 可以对数据打散,又可以保证数据分布均匀。

3.3.4 顺序分布与哈希分布的对比

Redis  部署--[扩展]实战:亿级Redis集群解决方案 - 图9

四、Redis Cluster基本架构

4.1 节点

Redis Cluster是分布式架构:即Redis Cluster中有多个节点,每个节点都负责进行数据读写操作,每个节点之间会进行通信。

4.2 meet操作

节点之间会相互通信,meet操作是节点之间完成相互通信的基础,meet操作有一定的频率和规则。
Redis  部署--[扩展]实战:亿级Redis集群解决方案 - 图10

4.3 分配槽

把16384个槽平均分配给节点进行管理,每个节点只能对自己负责的槽进行读写操作,由于每个节点之间都彼此通信,每个节点都知道另外节点负责管理的槽范围。
Redis  部署--[扩展]实战:亿级Redis集群解决方案 - 图11

客户端访问任意节点时,对数据key按照CRC16规则进行hash运算,然后对运算结果对16383进行取作,如果余数在当前访问的节点管理的槽范围内,则直接返回对应的数据,如果不在当前节点负责管理的槽范围内,则会告诉客户端去哪个节点获取数据,由客户端去正确的节点获取数据。

Redis  部署--[扩展]实战:亿级Redis集群解决方案 - 图12

4.4 复制

保证高可用,每个主节点都有一个从节点,当主节点故障,Cluster会按照规则实现主备的高可用性,对于节点来说,有一个配置项:cluster-enabled,即是否以集群模式启动。

4.5 客户端路由

4.5.1 moved重定向
1、每个节点通过通信都会共享Redis Cluster中槽和集群中对应节点的关系;
2、客户端向Redis Cluster的任意节点发送命令,接收命令的节点会根据CRC16规则进行hash运算与16383取余,计算自己的槽和对应节点;
3、如果保存数据的槽被分配给当前节点,则去槽中执行命令,并把命令执行结果返回给客户端;
4、如果保存数据的槽不在当前节点的管理范围内,则向客户端返回moved重定向异常;
5、客户端接收到节点返回的结果,如果是moved异常,则从moved异常中获取目标节点的信息;
6、客户端向目标节点发送命令,获取命令执行结果;

Redis  部署--[扩展]实战:亿级Redis集群解决方案 - 图13

需要注意的是:客户端不会自动找到目标节点执行命令。

4.5.2、槽命中:直接返回

Redis  部署--[扩展]实战:亿级Redis集群解决方案 - 图14

4.5.3、槽不命中:moved异常

Redis  部署--[扩展]实战:亿级Redis集群解决方案 - 图15

4.5.4 ask重定向

Redis  部署--[扩展]实战:亿级Redis集群解决方案 - 图16

在对集群进行扩容和缩容时,需要对槽及槽中数据进行迁移,当客户端向某个节点发送命令,节点向客户端返回moved异常,告诉客户端数据对应的槽的节点信息,如果此时正在进行集群扩展或者缩空操作,当客户端向正确的节点发送命令时,槽及槽中数据已经被迁移到别的节点了,就会返回ask,这就是ask重定向机制。

Redis  部署--[扩展]实战:亿级Redis集群解决方案 - 图17

步骤:
1、客户端向目标节点发送命令,目标节点中的槽已经迁移支别的节点上了,此时目标节点会返回ask转向给客户端
2、客户端向新的节点发送Asking命令给新的节点,然后再次向新节点发送命令
3、新节点执行命令,把命令执行结果返回给客户端

moved异常与ask异常的相同点和不同点:
1、两者都是客户端重定向
2、moved异常:槽已经确定迁移,即槽已经不在当前节点
3、ask异常:槽还在迁移中

4.5.5 smart智能客户端

使用智能客户端的首要目标:追求性能,从集群中选一个可运行节点,使用Cluster slots初始化槽和节点映射,将Cluster slots的结果映射在本地,为每个节点创建JedisPool,相当于为每个redis节点都设置一个JedisPool,然后就可以进行数据读写操作。

读写数据时的注意事项:
每个JedisPool中缓存了slot和节点node的关系。

key和slot的关系:对key进行CRC16规则进行hash后与16383取余得到的结果就是槽,JedisCluster启动时,已经知道key,slot和node之间的关系,可以找到目标节点,JedisCluster对目标节点发送命令,目标节点直接响应给JedisCluster,如果JedisCluster与目标节点连接出错,则JedisCluster会知道连接的节点是一个错误的节点,此时JedisCluster会随机节点发送命令,随机节点返回moved异常给JedisCluster。JedisCluster会重新初始化slot与node节点的缓存关系,然后向新的目标节点发送命令,目标命令执行命令并向JedisCluster响应,如果命令发送次数超过5次,则抛出异常"Too many cluster redirection!"

4.6 多节点命令实现

Redis Cluster不支持使用scan命令扫描所有节点,多节点命令就是在在所有节点上都执行一条命令,批量操作优化。

4.6.1 串行mget

定义for循环,遍历所有的key,分别去所有的Redis节点中获取值并进行汇总,简单,但是效率不高,需要n次网络时间
Redis  部署--[扩展]实战:亿级Redis集群解决方案 - 图18

4.6.2 串行IO

对串行mget进行优化,在客户端本地做内聚,对每个key进行CRC16hash,然后与16383取余,就可以知道哪个key对应的是哪个槽,本地已经缓存了槽与节点的对应关系,然后对key按节点进行分组,成立子集,然后使用pipeline把命令发送到对应的node,需要nodes次网络时间,大大减少了网络时间开销。
Redis  部署--[扩展]实战:亿级Redis集群解决方案 - 图19

4.6.3 并行IO

并行IO是对串行IO的一个优化,把key分组之后,根据节点数量启动对应的线程数,根据多线程模式并行向node节点请求数据,只需要1次网络时间。
Redis  部署--[扩展]实战:亿级Redis集群解决方案 - 图20

4.6.4 hash_tag

将key进行hash_tag的包装,然后把tag用大括号括起来,保证所有的key只向一个node请求数据,这样执行类似mget命令只需要去一个节点获取数据即可,效率更高。
Redis  部署--[扩展]实战:亿级Redis集群解决方案 - 图21

4.6.5 四种优化方案优缺点分析

Redis  部署--[扩展]实战:亿级Redis集群解决方案 - 图22

4.7 故障发现

Redis Cluster通过ping/pong消息实现故障发现:不需要sentinel.

ping/pong不仅能传递节点与槽的对应消息,也能传递其他状态,比如:节点主从状态,节点故障等,故障发现就是通过这种模式来实现,分为主观下线和客观下线。
Redis  部署--[扩展]实战:亿级Redis集群解决方案 - 图23

4.7.1 主观下线

某个节点认为另一个节点不可用,‘偏见’,只代表一个节点对另一个节点的判断,不代表所有节点的认知

主观下线流程:

1、节点1定期发送ping消息给节点2。

2、如果发送成功,代表节点2正常运行,节点2会响应PONG消息给节点1,节点1更新与节点2的最后通信时间。

3、如果发送失败,则节点1与节点2之间的通信异常判断连接,在下一个定时任务周期时,仍然会与节点2发送ping消息。

4、如果节点1发现与节点2最后通信时间超过node-timeout,则把节点2标识为pfail状态。

4.7.2 客观下线

当半数以上持有槽的主节点都标记某节点主观下线时,可以保证判断的公平性,集群模式下,只有主节点(master)才有读写权限和集群槽的维护权限,从节点(slave)只有复制的权限。

客观下线流程:

1、某个节点接收到其他节点发送的ping消息,如果接收到的ping消息中包含了其他pfail节点,这个节点会将主观下线的消息内容添加到自身的故障列表中,故障列表中包含了当前节点接收到的每一个节点对其他节点的状态信息。

2、当前节点把主观下线的消息内容添加到自身的故障列表之后,会尝试对故障节点进行客观下线操作。
故障列表的周期为:集群的node-timeout * 2,保证以前的故障消息不会对周期内的故障消息造成影响,保证客观下线的公平性和有效性。

Redis  部署--[扩展]实战:亿级Redis集群解决方案 - 图24

4.8 故障恢复

4.8.1 资格检查

对从节点的资格进行检查,只有难过检查的从节点才可以开始进行故障恢复,每个从节点检查与故障主节点的断线时间,超过cluster-node-timeout * cluster-slave-validity-factor数字,则取消资格,cluster-node-timeout默认为15秒,cluster-slave-validity-factor默认值为10,如果这两个参数都使用默认值,则每个节点都检查与故障主节点的断线时间,如果超过150秒,则这个节点就没有成为替换主节点的可能性。

4.8.2 准备选举时间

使偏移量最大的从节点具备优先级成为主节点的条件。
Redis  部署--[扩展]实战:亿级Redis集群解决方案 - 图25

4.8.3 选举投票

对选举出来的多个从节点进行投票,选出新的主节点。
Redis  部署--[扩展]实战:亿级Redis集群解决方案 - 图26

4.8.4 替换主节点

当前从节点取消复制变成离节点(slaveof no one)
执行cluster del slot撤销故障主节点负责的槽,并执行cluster add slot把这些槽分配给自己,向集群广播自己的pong消息,表明已经替换了故障从节点

4.9 Redis Cluster集群的缺点

当节点数量很多时,性能不会很高。

解决方式: 使用智能客户端。智能客户端知道由哪个节点负责管理哪个槽,而且当节点与槽的映射关系发生改变时,客户端也会知道这个改变,这是一种非常高效的方式。

五、Redis Cluster部署过程:

5.1、基本环境说明:(虚拟机测试环境,暂且使用6个节点6个实例模拟测试)

说明:使用redis版本是redis-5.0.4

172.21.18.72:6379
172.21.18.72:6380
173.172.18.90:6379
174.172.18.90:6380 
175.172.18.112:6379
176.172.18.112:6380

5.2、基本安装和配置:

# 参考:https://www.cnblogs.com/AzkbanHan/p/11497589.html

yum -y install gcc automake autoconf libtool make


tar zxvf redis-5.0.4.tar.gz
cd /test/kuaidi100/redis-5.0.4
make
cd /test/kuaidi100/redis-5.0.4/src/ && make && make PREFIX=/test/kuaidi100/redis install


[root@ceph-node02 kuaidi100]# grep -Ev "^#|^$" /test/kuaidi100/redis-5.0.4/redis.conf
bind 127.0.0.1
protected-mode yes
port 6379
tcp-backlog 511
timeout 0
tcp-keepalive 300
daemonize no
supervised no
pidfile /var/run/redis_6379.pid
loglevel notice
logfile ""
databases 16
always-show-logo yes
save 900 1
save 300 10
save 60 10000
stop-writes-on-bgsave-error yes
rdbcompression yes
rdbchecksum yes
dbfilename dump.rdb
dir ./
replica-serve-stale-data yes
replica-read-only yes
repl-diskless-sync no
repl-diskless-sync-delay 5
repl-disable-tcp-nodelay no
replica-priority 100
lazyfree-lazy-eviction no
lazyfree-lazy-expire no
lazyfree-lazy-server-del no
replica-lazy-flush no
appendonly no
appendfilename "appendonly.aof"
appendfsync everysec
no-appendfsync-on-rewrite no
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
aof-load-truncated yes
aof-use-rdb-preamble yes
lua-time-limit 5000
slowlog-log-slower-than 10000
slowlog-max-len 128
latency-monitor-threshold 0
notify-keyspace-events ""
hash-max-ziplist-entries 512
hash-max-ziplist-value 64
list-max-ziplist-size -2
list-compress-depth 0
set-max-intset-entries 512
zset-max-ziplist-entries 128
zset-max-ziplist-value 64
hll-sparse-max-bytes 3000
stream-node-max-bytes 4096
stream-node-max-entries 100
activerehashing yes
client-output-buffer-limit normal 0 0 0
client-output-buffer-limit replica 256mb 64mb 60
client-output-buffer-limit pubsub 32mb 8mb 60
hz 10
dynamic-hz yes
aof-rewrite-incremental-fsync yes
rdb-save-incremental-fsync yes

# 注意点
# :cluster-config-file须在集群内全局唯一,推荐节点号+端口格式,这个文件第一次启动会自动创建,但是文件名集群内要唯一

5.3、启动并创建集群

[root@ceph-node02 kuaidi100]#  /test/kuaidi100/redis/bin/redis-server  /test/kuaidi100/redis-6379/redis.conf
26011:C 16 May 2020 21:30:22.960 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
26011:C 16 May 2020 21:30:22.960 # Redis version=5.0.4, bits=64, commit=00000000, modified=0, pid=26011, just started
26011:C 16 May 2020 21:30:22.960 # Configuration loaded

[root@ceph-node02 kuaidi100]#  /test/kuaidi100/redis/bin/redis-server  /test/kuaidi100/redis-6380/redis.conf
26016:C 16 May 2020 21:30:43.083 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
26016:C 16 May 2020 21:30:43.083 # Redis version=5.0.4, bits=64, commit=00000000, modified=0, pid=26016, just started
26016:C 16 May 2020 21:30:43.083 # Configuration loaded

[root@ceph-node02 kuaidi100]# ps -ef|grep redis
root       26012       1  0 21:30 ?        00:00:00 /test/kuaidi100/redis/bin/redis-server 127.0.0.1:6379 [cluster]
root       26017       1  0 21:30 ?        00:00:00 /test/kuaidi100/redis/bin/redis-server 127.0.0.1:6380 [cluster]
root       26026   25447  0 21:31 pts/0    00:00:00 grep --color=auto redis

# 创建集群
[root@ceph-node01 ~]# /test/kuaidi100/redis/bin/redis-cli --cluster create  172.21.18.72:6379 172.21.18.72:6380 172.21.18.90:6379 172.21.18.90:6380  172.21.18.112:6379 172.21.18.112:6380  --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 172.21.18.90:6380 to 172.21.18.72:6379
Adding replica 172.21.18.112:6380 to 172.21.18.90:6379
Adding replica 172.21.18.72:6380 to 172.21.18.112:6379
M: 7695f170d9c1e0758f1be1475c8aa752e84d60db 172.21.18.72:6379
   slots:[0-5460] (5461 slots) master
S: 78305674f1fc975a6360e79cb10a372c9372f018 172.21.18.72:6380
   replicates 9eb176131ca20a5314175303bf8da859563af96c
M: c81eda2668b2930bc9fc0efe8a14ac887c82a7a1 172.21.18.90:6379
   slots:[5461-10922] (5462 slots) master
S: 4e6ddf13a4adda6f6569e24c6cfaa1ac94c55501 172.21.18.90:6380
   replicates 7695f170d9c1e0758f1be1475c8aa752e84d60db
M: 9eb176131ca20a5314175303bf8da859563af96c 172.21.18.112:6379
   slots:[10923-16383] (5461 slots) master
S: e3a662106ae698680ffbd9c2c483a80f69183069 172.21.18.112:6380
   replicates c81eda2668b2930bc9fc0efe8a14ac887c82a7a1
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 172.21.18.72:6379)
M: 5d539013684d524cdf3e7e51d8188d1475ce133e 172.21.18.72:6379
   slots:[0-5460] (5461 slots) master
   1 additional replica(s)
S: f2aafac9eef9eb18607ce8a0592293bc53214f74 172.21.18.90:6380
   slots: (0 slots) slave
   replicates 5d539013684d524cdf3e7e51d8188d1475ce133e
M: 7a4a9ca1277377e237fc2ec021148d6d0c0bdf92 172.21.18.112:6379
   slots:[10923-16383] (5461 slots) master
   1 additional replica(s)
M: 9e73bc9907c56752d25c3b0ba4c934f2b706b595 172.21.18.90:6379
   slots:[5461-10922] (5462 slots) master
   1 additional replica(s)
S: 85048b8c60c5f1e03a166a0c28b7a36ce77aa0da 172.21.18.72:6380
   slots: (0 slots) slave
   replicates 7a4a9ca1277377e237fc2ec021148d6d0c0bdf92
S: 27a1f6080d0bbb373f2fc1ad21d52b232a431146 172.21.18.112:6380
   slots: (0 slots) slave
   replicates 9e73bc9907c56752d25c3b0ba4c934f2b706b595
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.


# 如果第一次创建集群失败,然后重启创建报如下错误:
[ERR] Node 172.21.18.72:6379 is not empty. Either the node already knows other nodes (check with CLUSTER NODES) or contains some key in database 0.

# 解决:  删除每个节点下的其他数据和生成的配置文件,只保留启动的配置文件,然后重启启动每个实例,然后重启创建集群就好了。

[root@ceph-node01 kuaidi100]# ./redis/bin/redis-cli cluster nodes
f2aafac9eef9eb18607ce8a0592293bc53214f74 172.21.18.90:6380@16380 slave 5d539013684d524cdf3e7e51d8188d1475ce133e 0 1589638497178 4 connected
7a4a9ca1277377e237fc2ec021148d6d0c0bdf92 172.21.18.112:6379@16379 master - 0 1589638499183 5 connected 10923-16383
9e73bc9907c56752d25c3b0ba4c934f2b706b595 172.21.18.90:6379@16379 master - 0 1589638498180 3 connected 5461-10922
85048b8c60c5f1e03a166a0c28b7a36ce77aa0da 172.21.18.72:6380@16380 slave 7a4a9ca1277377e237fc2ec021148d6d0c0bdf92 0 1589638497000 5 connected
5d539013684d524cdf3e7e51d8188d1475ce133e 172.21.18.72:6379@16379 myself,master - 0 1589638497000 1 connected 0-5460
27a1f6080d0bbb373f2fc1ad21d52b232a431146 172.21.18.112:6380@16380 slave 9e73bc9907c56752d25c3b0ba4c934f2b706b595 0 1589638498000 6 connected


# 测试
[root@ceph-node01 kuaidi100]# ./redis/bin/redis-cli -c   // -c 以集群的模式进入

127.0.0.1:6379> set k1 v1
-> Redirected to slot [12706] located at 172.21.18.72:6380
OK
172.21.18.72:6380> set k2 v2
-> Redirected to slot [449] located at 172.21.18.72:6379
OK
172.21.18.72:6379> get k1
-> Redirected to slot [12706] located at 172.21.18.72:6380
"v1"
172.21.18.72:6380> get k2
-> Redirected to slot [449] located at 172.21.18.72:6379
"v2"
172.21.18.72:6379>


# 当我手动停掉其中一个实例的master,就有2个实例为master在同一个vm节点
172.21.18.72:6379> CLUSTER nodes
f2aafac9eef9eb18607ce8a0592293bc53214f74 172.21.18.90:6380@16380 slave 5d539013684d524cdf3e7e51d8188d1475ce133e 0 1589639005331 4 connected
7a4a9ca1277377e237fc2ec021148d6d0c0bdf92 172.21.18.112:6379@16379 master,fail - 1589638754676 1589638752000 5 disconnected
9e73bc9907c56752d25c3b0ba4c934f2b706b595 172.21.18.90:6379@16379 master - 0 1589639004000 3 connected 5461-10922
85048b8c60c5f1e03a166a0c28b7a36ce77aa0da 172.21.18.72:6380@16380 master - 0 1589639003324 8 connected 10923-16383
5d539013684d524cdf3e7e51d8188d1475ce133e 172.21.18.72:6379@16379 myself,master - 0 1589639003000 1 connected 0-5460
27a1f6080d0bbb373f2fc1ad21d52b232a431146 172.21.18.112:6380@16380 slave 9e73bc9907c56752d25c3b0ba4c934f2b706b595 0 1589639005000 6 connected

# 然后在其启动刚刚停掉的实例:

172.21.18.72:6379> CLUSTER nodes
f2aafac9eef9eb18607ce8a0592293bc53214f74 172.21.18.90:6380@16380 slave 5d539013684d524cdf3e7e51d8188d1475ce133e 0 1589639078000 4 connected
7a4a9ca1277377e237fc2ec021148d6d0c0bdf92 172.21.18.112:6379@16379 slave 85048b8c60c5f1e03a166a0c28b7a36ce77aa0da 0 1589639081526 8 connected
9e73bc9907c56752d25c3b0ba4c934f2b706b595 172.21.18.90:6379@16379 master - 0 1589639080525 3 connected 5461-10922
85048b8c60c5f1e03a166a0c28b7a36ce77aa0da 172.21.18.72:6380@16380 master - 0 1589639079000 8 connected 10923-16383
5d539013684d524cdf3e7e51d8188d1475ce133e 172.21.18.72:6379@16379 myself,master - 0 1589639080000 1 connected 0-5460
27a1f6080d0bbb373f2fc1ad21d52b232a431146 172.21.18.112:6380@16380 slave 9e73bc9907c56752d25c3b0ba4c934f2b706b595 0 1589639080000 6 connected

5.4、关于cluster node 输出信息说明:

[root@ceph-node01 ~]# redis-cli -c -p 6379 cluster nodes
7a4a9ca1277377e237fc2ec021148d6d0c0bdf92 172.21.18.112:6379@16379 master - 0 1590043129000 11 connected 10923-16383
9e73bc9907c56752d25c3b0ba4c934f2b706b595 172.21.18.90:6379@16379 slave 27a1f6080d0bbb373f2fc1ad21d52b232a431146 0 1590043128000 12 connected
27a1f6080d0bbb373f2fc1ad21d52b232a431146 172.21.18.112:6380@16380 master - 0 1590043129981 12 connected 5461-10922
85048b8c60c5f1e03a166a0c28b7a36ce77aa0da 172.21.18.72:6380@16380 slave 7a4a9ca1277377e237fc2ec021148d6d0c0bdf92 0 1590043127000 11 connected
f2aafac9eef9eb18607ce8a0592293bc53214f74 172.21.18.90:6380@16380 slave 5d539013684d524cdf3e7e51d8188d1475ce133e 0 1590043129000 10 connected
5d539013684d524cdf3e7e51d8188d1475ce133e 172.21.18.72:6379@16379 myself,master - 0 1590043128000 10 connected 0-5460

# 格式:
<id> <ip:port> <flags> <master> <ping-sent> <pong-recv> <config-epoch> <link-state> <slot> <slot> ... <slot>

# 请参考:
http://www.redis.cn/commands/cluster-nodes.html

Redis  部署--[扩展]实战:亿级Redis集群解决方案 - 图27

5.6、测试redis集群哈希槽位迁移(数据迁移)

# yum install ruby* -y  //这个是默认的域名安装,如果使用高版本的请往下查看:

[root@ceph-node01 ~]# /test/kuaidi100/redis-5.0.4/src/redis-cli cluster nodes  //查看每个节点状态和槽位数:
7a4a9ca1277377e237fc2ec021148d6d0c0bdf92 172.21.18.112:6379@16379 master - 0 1590053311000 11 connected
9e73bc9907c56752d25c3b0ba4c934f2b706b595 172.21.18.90:6379@16379 slave 5d539013684d524cdf3e7e51d8188d1475ce133e 0 1590053312514 13 connected
27a1f6080d0bbb373f2fc1ad21d52b232a431146 172.21.18.112:6380@16380 master - 0 1590053310510 12 connected
85048b8c60c5f1e03a166a0c28b7a36ce77aa0da 172.21.18.72:6380@16380 slave 5d539013684d524cdf3e7e51d8188d1475ce133e 0 1590053311512 13 connected
f2aafac9eef9eb18607ce8a0592293bc53214f74 172.21.18.90:6380@16380 slave 5d539013684d524cdf3e7e51d8188d1475ce133e 0 1590053309508 13 connected
5d539013684d524cdf3e7e51d8188d1475ce133e 172.21.18.72:6379@16379 myself,master - 0 1590053311000 13 connected 0-16383


# 查看节点key信息:

[root@ceph-node01 ~]# /test/kuaidi100/redis-5.0.4/src/redis-cli  --cluster  info 127.0.0.1:6380
172.21.18.112:6380 (27a1f608...) -> 0 keys | 0 slots | 0 slaves.
172.21.18.72:6379 (5d539013...) -> 4 keys | 16384 slots | 3 slaves.
172.21.18.112:6379 (7a4a9ca1...) -> 0 keys | 0 slots | 0 slaves.


[root@ceph-node01 ~]# /test/kuaidi100/redis-5.0.4/src/redis-cli  --cluster  reshard  172.21.18.112:6379
>>> Performing Cluster Check (using node 172.21.18.112:6379)
M: 7a4a9ca1277377e237fc2ec021148d6d0c0bdf92 172.21.18.112:6379
   slots:[0-499] (500 slots) master
   1 additional replica(s)
M: 5d539013684d524cdf3e7e51d8188d1475ce133e 172.21.18.72:6379
   slots:[500-16383] (15884 slots) master
   2 additional replica(s)
M: 27a1f6080d0bbb373f2fc1ad21d52b232a431146 172.21.18.112:6380
   slots: (0 slots) master
S: 85048b8c60c5f1e03a166a0c28b7a36ce77aa0da 172.21.18.72:6380
   slots: (0 slots) slave
   replicates 7a4a9ca1277377e237fc2ec021148d6d0c0bdf92
S: f2aafac9eef9eb18607ce8a0592293bc53214f74 172.21.18.90:6380
   slots: (0 slots) slave
   replicates 5d539013684d524cdf3e7e51d8188d1475ce133e
S: 9e73bc9907c56752d25c3b0ba4c934f2b706b595 172.21.18.90:6379
   slots: (0 slots) slave
   replicates 5d539013684d524cdf3e7e51d8188d1475ce133e
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
How many slots do you want to move (from 1 to 16384)? 500    //问你本次想要移动多少个槽位
What is the receiving node ID? 27a1f6080d0bbb373f2fc1ad21d52b232a431146   //想把槽位移动到哪个master节点
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: 5d539013684d524cdf3e7e51d8188d1475ce133e    //原来那些槽位所在的节点,就是来源哪里
Source node #2: done      //开始移动

Redis  部署--[扩展]实战:亿级Redis集群解决方案 - 图28

5.7、集群新增slave节点:

# 172.21.30.119:6379: 即将要加入的新的节点
# 172.21.30.119:6380: 新加入slave节点

[root@ceph-node01 ~]# /test/kuaidi100/redis-5.0.4/src/redis-cli  --cluster add-node --cluster-slave --cluster-master-id db10a9d5c1662d9e3cee21c5776f2e9709f76619 172.21.30.119:6379172.21.30.119:6380

六、关于redis cluster集群的监控:


# 通过以下命令可以获取redis集群的所有信息,然后通过shell脚本+zabbix进行监控,通过grafana可视化展示:
[root@ceph-node01 ~]# /test/kuaidi100/redis-5.0.4/src/redis-cli  -h 172.21.30.112 -c -a password   info

集群监控图实例:
Redis  部署--[扩展]实战:亿级Redis集群解决方案 - 图29

七、关于Redis Cluster总结:

1、Redis Cluster数据分区规则采用虚拟槽方式(16384个槽),每个节点负责一部分槽和相关数据,实现数据和请求的负载均衡

2、搭建Redis Cluster划分四个步骤:准备节点,meet操作,分配槽,复制数据。

3、Redis官方推荐使用redis-trib.rb工具快速搭建Redis Cluster

4、集群伸缩通过在节点之间移动槽和相关数据实现,扩容时根据槽迁移计划把槽从源节点迁移到新节点,收缩时如果下线的节点有负责的槽需要迁移到其他节点,再通过cluster forget命令让集群内所有节点忘记被下线节点

5、使用smart客户端操作集群过到通信效率最大化,客户端内部负责计算维护键,槽以及节点的映射,用于快速定位到目标节点

6、集群自动故障转移过程分为故障发现和节点恢复。节点下线分为主观下线和客观下线,当超过半数节点认为故障节点为主观下线时,标记这个节点为客观下线状态。从节点负责对客观下线的主节点触发故障恢复流程,保证集群的可用性