Redis Cluster集群(5.x)

  • 1. Redis Cluster集群原理
  • 1.1. Cluster架构概念
  • 1.1.1. Redis 哨兵集群的不足
  • 1.1.2. Redis Cluster 适用场景
  • 1.1.3. 没有 Redis Cluster 前
  • 1.1.4. Cluster 数据分布式
  • 1.1.5. Cluster 重要概念
  • 1.2. Redis Cluster 架构图
  • 1.2.1. 不合理架构图
  • 1.2.2. 合理的架构图
  • 2. 手动部署 Redis 集群
  • 2.1. 部署一个三主三从集群
  • 2.2. 实验环境架构
  • 2.3. 部署 Redis 实例
  • 2.3.1. 51主机的操作
  • 2.3.2. 52主机的操作
  • 2.3.3. 53主机的操作
  • 2.3.4. 查看 redis 进程
  • 2.3.5. 查看集群文件
  • 2.4. 集群手动发现节点
  • 2.5. 集群槽位分配
  • 2.5.1. Cluster通讯流程
  • 2.5.2. 集群手动分配槽位
  • 2.6. 手动部署复制关系
  • 2.6.1. 配置主从交叉式复制
  • 2.6.2. 检查复制关系
  • 3. Redis Cluster 集群验证
  • 3.1. 尝试插入数据
  • 3.2. 问题原因
  • 3.3. 验证集群是否足够足迹足够平均
  • 3.4. 检查集群健康状态
  • 3.5. Redis Cluster 集群的命令帮助
  • 4. 模拟故障转移
  • 4.1. 模拟 db01:6381 坏掉
  • 4.2. db01 节点的主库恢复前的架构图
  • 4.3. 将恢复的故障节点重新变成主
  • 5. Redis Cluster 需要注意
  • 6. Redis Cluster 小结


1. Redis Cluster集群原理

1.1. Cluster架构概念

Redis Cluster 是 Redis 的分布式解决方案,在 3.0 版本正式推出,如果公司的 Redis 是 2.x 版本,就需要升级了

1.1.1. Redis 哨兵集群的不足

  1. 资源利用率低,只有一台主库对外提供服务
    假如哨兵有 3 个节点,只有其中一个节点对外提供服务,其他两个节点都处于备用的状态,这样就会造成资源的浪费,Redis 将数据都写在内里,只有主节点工作,那么内存只有主节点上的那点内存空间,而从节点上的内存也就造成了浪费。
  2. 主库压力比较大,性能有瓶颈
    只有主库在工作,所有写操作都是主节点完成,这样就会造成主节点的压力比较大,从节点几乎没有压力。
  3. 连接过程繁琐

哨兵的不足之处就在是集群不是分布式存储,只有单节点去处理,如果集群是分布式存储,那么单点和压力的问题也就解决了

1.1.2. Redis Cluster 适用场景

当 Redis 遇到单机、内存、并发、流量等瓶颈,可以采用 Cluster 架构方案达到负载均衡的目的

1.1.3. 没有 Redis Cluster 前

1. 客户端分区方案
	优点:分区逻辑可控
	缺点:需要自己处理数据路由,高可用和故障转移等
2. 代理方案(Twemproxy、Codis)
	优点:简化客户端分布式逻辑和升级维护方便
	缺点:加重架构部署和性能消耗

1.1.4. Cluster 数据分布式

所有分布式数据首先要解决把整个数据库集群安装分区规则映射到多个节点的问题,也就是把数据集划分到多个节点上,Redis Cluster采用哈希分片规则,这样就能确保每个节点存储的数据量大致相同

1.1.5. Cluster 重要概念

1. Redis 集群中无论有几个节点,一共只有 16384 个槽位
2. 所有槽位都必须分配,哪怕有 1 个槽位不正常,珍格格集群都是不能用的
3. 每个节点的槽的顺序不重要,重要的数据
4. Hash 算法足够随机,足够平均
5. 每个槽被份到数据的概率是相当的
6. 集群的高可用依赖于主从复制
7. 集群拥有自己的配置文件,动态更新,不要手动修改
8. 集群通讯会使用基础端口 + 10000 的端口,这个是自动创建的,不是配置文件配置的
9. 集群槽位分配比例运行误差在 2% 之间

1.2. Redis Cluster 架构图

1.2.1. 不合理架构图

Cluster 架构中每个节点上都有多个 master 和 slave,如果 master 节点的数据备份都在自己主机的 slave 上,那么当主机坏掉后,这个主机上的数据就会丢失,数据丢失真个应用就会崩溃了

redis查看集群信息命令 redis查看集群状态_redis查看集群信息命令

1.2.2. 合理的架构图

每个节点slave都存放在别的主机,即使当前主机挂掉,另一台直接还原数据即可

redis查看集群信息命令 redis查看集群状态_Redis_02

2. 手动部署 Redis 集群

2.1. 部署一个三主三从集群

1. 在三台主机上部署 Redis,分别启动两个不同端口的 Redis,一个主库一个从库
2. 配置集群自动发现,使得集群中各个节点都知道其他节点上的 Redis
3. 配置集群 Hash 分配槽位,有了槽位才能存储数据
4. 使用 cluster replicate 使多出来的三个主库变成从库,这样就实现三主三从

2.2. 实验环境架构

三台主机:

  • db01: 192.168.66.51
  • db02: 192.168.66.52
  • db03: 192.168.66.53

主节点端口:6380
从节点端口:6381

redis查看集群信息命令 redis查看集群状态_Redis_03

2.3. 部署 Redis 实例

Redis Cluster 中的 Redis 实例不能绑定 127.0.0.1,否则的话 Redis 实例之间无法自动发现

2.3.1. 51主机的操作

# 配置免秘钥登录
ssh-keygen -t rsa -N '' -f ~/.ssh/id_rsa
ssh-copy-id 192.168.66.52
ssh-copy-id 192.168.66.53

# 创建相关目录
mkdir -p /opt/redis/{6381,6382}/{etc,logs,pid}
mkdir -p /data/redis/{6381,6382}

# 创建配置文件
cat > /opt/redis/6381/etc/6381.conf << EOF
daemonize yes
bind 192.168.66.51
port 6381
pidfile /opt/redis/6381/pid/6381.pid
logfile /opt/redis/6381/logs/6381.log
dir "/data/redis/6381"
dbfilename "6381.rdb"
save 60 10000
save 300 10
save 900 1
appendonly yes
appendfilename "6381.aof"
appendfsync everysec
# 开启集群模式
cluster-enabled yes
# 集群数据文件路径,保存集群信息的文件
cluster-config-file node_6381.conf
# 集群故障转移时间,多长时间无响应就切换
cluster-node-timeout 15000
EOF
cp /opt/redis/6381/etc/6381.conf /opt/redis/6382/etc/6382.conf
sed -i 's#6381#6382#g' /opt/redis/6382/etc/6382.conf
chown -R redis. /data/redis /opt/redis

# 配置system
cat > /usr/lib/systemd/system/redis-master.service << EOF
[Unit]
Description=Redis persistent key-value database
After=network.target
After=network-online.target
Wants=network-online.target

[Service]
ExecStart=/usr/local/redis/bin/redis-server /opt/redis/6381/etc/6381.conf --supervised systemd
ExecStop=/usr/local/redis/bin/redis-shutdown 6381
Type=notify
User=redis
Group=redis
RuntimeDirectory=redis
RuntimeDirectoryMode=0755

[Install]
WantedBy=multi-user.target
EOF

cat > /usr/lib/systemd/system/redis-slave.service << EOF
[Unit]
Description=Redis persistent key-value database
After=network.target
After=network-online.target
Wants=network-online.target

[Service]
ExecStart=/usr/local/redis/bin/redis-server /opt/redis/6382/etc/6382.conf --supervised systemd
ExecStop=/usr/local/redis/bin/redis-shutdown 6381
Type=notify
User=redis
Group=redis
RuntimeDirectory=redis
RuntimeDirectoryMode=0755

[Install]
WantedBy=multi-user.target
EOF

# 启动服务
systemctl daemon-reload
systemctl start redis-master.service
systemctl start redis-slave.service
ps aux|grep redis

# 拷贝到另外两个节点
rsync -avz /opt/redis/638* 192.168.66.52:/opt/redis
rsync -avz /opt/redis/638* 192.168.66.53:/opt/redis
rsync -avz /usr/local/redis/bin/redis-shutdown 192.168.66.52:/usr/local/redis/bin/
rsync -avz /usr/local/redis/bin/redis-shutdown 192.168.66.53:/usr/local/redis/bin/
rsync -avz /usr/lib/systemd/system/redis-{master,slave}.service 192.168.66.52:/usr/lib/systemd/system/
rsync -avz /usr/lib/systemd/system/redis-{master,slave}.service 192.168.66.53:/usr/lib/systemd/system/

2.3.2. 52主机的操作

find /opt/redis/638* -type f -name "*.conf"|xargs sed -i 's#51#52#g'
mkdir -p /data/redis/{6381,6382}
chown -R redis. /data/redis /opt/redis
systemctl daemon-reload
systemctl start redis-master.service
systemctl start redis-slave.service
ps aux|grep redis

2.3.3. 53主机的操作

find /opt/redis/638* -type f -name "*.conf"|xargs sed -i 's#51#53#g'
mkdir -p /data/redis/{6381,6382}
chown -R redis. /data/redis /opt/redis
systemctl daemon-reload
systemctl start redis-master.service
systemctl start redis-slave.service
ps aux|grep redis

2.3.4. 查看 redis 进程

每个节点启动了 cluster 后,进程名上增加 cluster,并且每个 Redis 节点会开放 6381,集群通信端口16381(在服务端口基础上增加10000)

~]# netstat -nlutp|grep redis
tcp        0      0 192.168.66.51:16381     0.0.0.0:*               LISTEN      14581/redis-server
tcp        0      0 192.168.66.51:16382     0.0.0.0:*               LISTEN      14591/redis-server
tcp        0      0 192.168.66.51:6381      0.0.0.0:*               LISTEN      14581/redis-server
tcp        0      0 192.168.66.51:6382      0.0.0.0:*               LISTEN      14591/redis-server

~]# ps aux|grep redis|grep -v grep
redis     14581  0.4  0.8 153996  8200 ?        Ssl  19:01   0:00 /usr/local/redis/bin/redis-server 192.168.66.51:6381 [cluster]
redis     14591  0.1  0.8 153996  8196 ?        Ssl  19:01   0:00 /usr/local/redis/bin/redis-server 192.168.66.51:6382 [cluster]

2.3.5. 查看集群文件

集群模式的 Redis 除了原有的配置文件之外又增加了一个集群配置文件,当集群内节点信息发生变化时,如添加节点,节点下线,故障转移等,节点都会自动保存集群状态到配置文件,Redis 自动维护集群配置文件,不需要手动修改防止节点重启时产生错了

在集群启动后悔生成一个数据文件,这个文件其实保存的就是集群的信息,在没有配置集群互相发现时,单个节点只保存自己的集群信息,文件中有节点 id 信息,每个节点的 id 都是唯一的

当配置了相互发现了配置文件中会自动增加节点的信息

~]# cat /data/redis/6381/node_6381.conf
4905ba18001a92c96e08b6759e5fe374ea6f03d8 :0@0 myself,master - 0 0 0 connected
vars currentEpoch 0 lastVoteEpoch 0

也可以登录到 Redis 查看集群信息

~]# for ip in 192.168.66.{51..53}; do echo "=== ${ip}:6381 ==="; redis-cli -h $ip -p 6381 cluster nodes; echo "=== ${ip}:6382 ==="; redis-cli -h $ip -p 6382 cluster nodes; done
=== 192.168.66.51:6381 ===
4905ba18001a92c96e08b6759e5fe374ea6f03d8 :6381@16381 myself,master - 0 0 0 connected
=== 192.168.66.51:6382 ===
f8ee3d4c4019f9a03e56cd913b2c3881f41b5586 :6382@16382 myself,master - 0 0 0 connected
=== 192.168.66.52:6381 ===
98ac4f577aaaff768c65116f64da76f3bf3eaaa7 :6381@16381 myself,master - 0 0 0 connected
=== 192.168.66.52:6382 ===
c832842e805bfc7f62e9789d7d2e1f1d78798315 :6382@16382 myself,master - 0 0 0 connected
=== 192.168.66.53:6381 ===
5e35d4fc44ff1389eaa13b06547a4ae7b7391040 :6381@16381 myself,master - 0 0 0 connected
=== 192.168.66.53:6382 ===
042c2048aa5a05821f9cc2a06618e5c8c63a6966 :6382@16382 myself,master - 0 0 0 connected

2.4. 集群手动发现节点

# 1. 手动发现节点
redis-cli -h 192.168.66.51 -p 6381 CLUSTER MEET 192.168.66.52 6381
redis-cli -h 192.168.66.51 -p 6381 CLUSTER MEET 192.168.66.53 6381
redis-cli -h 192.168.66.51 -p 6381 CLUSTER MEET 192.168.66.51 6382
redis-cli -h 192.168.66.51 -p 6381 CLUSTER MEET 192.168.66.52 6382
redis-cli -h 192.168.66.51 -p 6381 CLUSTER MEET 192.168.66.53 6382

# 2. 查看集群信息
~]# redis-cli -h 192.168.66.51 -p 6381 CLUSTER NODES
c832842e805bfc7f62e9789d7d2e1f1d78798315 192.168.66.52:6382@16382 master - 0 1649589196000 4 connected
4905ba18001a92c96e08b6759e5fe374ea6f03d8 192.168.66.51:6381@16381 myself,master - 0 1649589194000 1 connected
042c2048aa5a05821f9cc2a06618e5c8c63a6966 192.168.66.53:6382@16382 master - 0 1649589195173 3 connected
98ac4f577aaaff768c65116f64da76f3bf3eaaa7 192.168.66.52:6381@16381 master - 0 1649589196179 5 connected
f8ee3d4c4019f9a03e56cd913b2c3881f41b5586 192.168.66.51:6382@16382 master - 0 1649589196000 0 connected
5e35d4fc44ff1389eaa13b06547a4ae7b7391040 192.168.66.53:6381@16381 master - 0 1649589197184 2 connected

# 3. 查看集群文件是否正价内容
~]# cat /data/redis/6381/node_6381.conf
c832842e805bfc7f62e9789d7d2e1f1d78798315 192.168.66.52:6382@16382 master - 0 1649589188227 4 connected
4905ba18001a92c96e08b6759e5fe374ea6f03d8 192.168.66.51:6381@16381 myself,master - 0 1649589157000 1 connected
042c2048aa5a05821f9cc2a06618e5c8c63a6966 192.168.66.53:6382@16382 master - 0 1649589188630 3 connected
98ac4f577aaaff768c65116f64da76f3bf3eaaa7 192.168.66.52:6381@16381 master - 0 1649589188228 5 connected
f8ee3d4c4019f9a03e56cd913b2c3881f41b5586 192.168.66.51:6382@16382 master - 0 1649589188227 0 connected
5e35d4fc44ff1389eaa13b06547a4ae7b7391040 192.168.66.53:6381@16381 master - 0 1649589188227 2 connected
vars currentEpoch 5 lastVoteEpoch 0

2.5. 集群槽位分配

2.5.1. Cluster通讯流程

集群内消息传递是同步的

在分布式存储中需要提供节点元数据信息的机制,所谓元数据是指:节点负责哪些数据,是否出现故障状态信息,Redis 集群采用 gossip 协议,gossip 协议工作原理就是节点彼此不断交换信息,一段时间后所有节点偶会指定集群完整信息,这种方式类似流言传播,因此只需要在一台节点配置集群信息所有节点都能收到信息

通讯过程:

  1. 集群中的每一个节点都会单独开辟一个 TCP 通道用于节点之间彼此通信,通信端口在基础端口上增加 10000
  2. 每个节点在固定周期内通过特定规则选择结构节点发送 ping 消息
  3. 接收到 ping 消息的节点用 pong 作为消息响应,集群中每个节点通过一定规则挑选要通信的节点,每个节点可能知道全部节点的信息,也可能知道部分节点信息,只要这些节点彼此可以正常通信,最终他们就会达成一致的状态,当节点出现故障,新节点加入,主从角色变化等,彼此之间不断发生 ping/pong 消息,最终达成同步的模板

通讯消息类型:gossip,信息交换,常见的消息分为 ping、pong、meet、fail

没有分配槽位时的集群状态,所有节点执行 cluster infocluster_state 都是 fail,fail 状态表示集群不可用,没有分配槽位,cluster_slots* 都会显示 0

~]# redis-cli -h 192.168.66.51 -p 6381 CLUSTER INFO
cluster_state:fail
cluster_slots_assigned:0
cluster_slots_ok:0
cluster_slots_pfail:0
cluster_slots_fail:0
cluster_known_nodes:6
cluster_size:0
cluster_current_epoch:5
cluster_my_epoch:1
cluster_stats_messages_ping_sent:534
cluster_stats_messages_pong_sent:536
cluster_stats_messages_meet_sent:15
cluster_stats_messages_sent:1085
cluster_stats_messages_ping_received:536
cluster_stats_messages_pong_received:549
cluster_stats_messages_received:1085

2.5.2. 集群手动分配槽位

之前我们介绍过 Redis Cluster 集群都有 16384 个槽位,我们有三台主机,想要手动配置平均就需要使用 16384/3

db01: 6381 5461  0-5460
db02: 6381 5461  5461-10921
db03: 6381 5462  10922-16383

分配槽位语法格式(交互式):CLUSTER ADDSLOTS 0 5461 分配槽位语法:redis-cli -h 192.168.66.51 -p 6381 CLUSTER ADDSLOTS {0..5460} 删除槽位分配语法格式: redis-cli -h 192.168.81.210 -p 6380 CLUSTER DELSLOTS {5463…10921}

# 1. 手动分配槽位
redis-cli -h 192.168.66.51 -p 6381 CLUSTER ADDSLOTS {0..5460}
redis-cli -h 192.168.66.52 -p 6381 CLUSTER ADDSLOTS {5461..10921}
redis-cli -h 192.168.66.53 -p 6381 CLUSTER ADDSLOTS {10922..16383}

# 2. 查看集群状态
~]# redis-cli -h 192.168.66.51 -p 6381 CLUSTER INFO
~]# redis-cli -h 192.168.66.51 -p 6381 CLUSTER INFO
cluster_state:ok
cluster_slots_assigned:16384
cluster_slots_ok:16384
cluster_slots_pfail:0
cluster_slots_fail:0
cluster_known_nodes:6
cluster_size:3
cluster_current_epoch:5
cluster_my_epoch:1
cluster_stats_messages_ping_sent:556
cluster_stats_messages_pong_sent:553
cluster_stats_messages_meet_sent:15
cluster_stats_messages_sent:1124
cluster_stats_messages_ping_received:553
cluster_stats_messages_pong_received:571
cluster_stats_messages_received:1124

# 3. 查看 nodes 信息
~]# redis-cli -h 192.168.66.51 -p 6381 CLUSTER NODES
c832842e805bfc7f62e9789d7d2e1f1d78798315 192.168.66.52:6382@16382 master - 0 1649589267000 4 connected
4905ba18001a92c96e08b6759e5fe374ea6f03d8 192.168.66.51:6381@16381 myself,master - 0 1649589265000 1 connected 0-5460
042c2048aa5a05821f9cc2a06618e5c8c63a6966 192.168.66.53:6382@16382 master - 0 1649589267643 3 connected
98ac4f577aaaff768c65116f64da76f3bf3eaaa7 192.168.66.52:6381@16381 master - 0 1649589264624 5 connected 5461-10921
f8ee3d4c4019f9a03e56cd913b2c3881f41b5586 192.168.66.51:6382@16382 master - 0 1649589268649 0 connected
5e35d4fc44ff1389eaa13b06547a4ae7b7391040 192.168.66.53:6381@16381 master - 0 1649589267000 2 connected 10922-16383

2.6. 手动部署复制关系

配置三主三从规范操作步骤:

  1. 将集群信息粘贴到 txt 文本中,只留下 6381 端口的信息
  2. 配置命令在 txt 中准备好在复制到命令行

主节点我们已经有了,目前6个节点全是主节点,我们需要把所有主机的 6382 的主节点配置成从节点

从节点对应的主节点关系:

  • db01 的 6382 从节点对应的主节点是 db03 的 6381 主节点
  • db02 的 6382 从节点对应的主节点是 db01 的 6381 主节点
  • db03 的 6382 从节点对应的主节点是 db02 的 6381 主节点
# 1. 将集群信息粘贴到 txt 文本中,只留下 6381 端口的信息
~]# redis-cli -h 192.168.66.51 -p 6381 CLUSTER NODES|grep 6381@
4905ba18001a92c96e08b6759e5fe374ea6f03d8 192.168.66.51:6381@16381 myself,master - 0 1649589285000 1 connected 0-5460
98ac4f577aaaff768c65116f64da76f3bf3eaaa7 192.168.66.52:6381@16381 master - 0 1649589284750 5 connected 5461-10921
5e35d4fc44ff1389eaa13b06547a4ae7b7391040 192.168.66.53:6381@16381 master - 0 1649589286000 2 connected 10922-16383

# 2. 配置命令在 txt 中准备好在复制到命令行
redis-cli -h 192.168.66.51 -p 6382 CLUSTER REPLICATE 5e35d4fc44ff1389eaa13b06547a4ae7b7391040 # 53的6381的ID
redis-cli -h 192.168.66.52 -p 6382 CLUSTER REPLICATE 4905ba18001a92c96e08b6759e5fe374ea6f03d8 # 51的6381的ID
redis-cli -h 192.168.66.53 -p 6382 CLUSTER REPLICATE 98ac4f577aaaff768c65116f64da76f3bf3eaaa7 # 52的6381的ID

CLUSTER REPLICATE 是配置当前节点成为某个主节点的从节点,replicate 命令其实就相当于执行了slaveof ,同步了某一个主库,并且在日志中查看到的就是主从同步的过程

2.6.1. 配置主从交叉式复制

~]# redis-cli -h 192.168.66.51 -p 6382 CLUSTER REPLICATE 5e35d4fc44ff1389eaa13b06547a4ae7b7391040 # 53的6381的ID
OK
~]# redis-cli -h 192.168.66.52 -p 6382 CLUSTER REPLICATE 4905ba18001a92c96e08b6759e5fe374ea6f03d8 # 51的6381的ID
OK
~]# redis-cli -h 192.168.66.53 -p 6382 CLUSTER REPLICATE 98ac4f577aaaff768c65116f64da76f3bf3eaaa7 # 52的6381的ID
OK

2.6.2. 检查复制关系

~]# redis-cli -h 192.168.66.51 -p 6381 CLUSTER NODES
c832842e805bfc7f62e9789d7d2e1f1d78798315 192.168.66.52:6382@16382 slave 4905ba18001a92c96e08b6759e5fe374ea6f03d8 0 1649589373000 4 connected
4905ba18001a92c96e08b6759e5fe374ea6f03d8 192.168.66.51:6381@16381 myself,master - 0 1649589374000 1 connected 0-5460
042c2048aa5a05821f9cc2a06618e5c8c63a6966 192.168.66.53:6382@16382 slave 98ac4f577aaaff768c65116f64da76f3bf3eaaa7 0 1649589374337 5 connected
98ac4f577aaaff768c65116f64da76f3bf3eaaa7 192.168.66.52:6381@16381 master - 0 1649589375344 5 connected 5461-10921
f8ee3d4c4019f9a03e56cd913b2c3881f41b5586 192.168.66.51:6382@16382 slave 5e35d4fc44ff1389eaa13b06547a4ae7b7391040 0 1649589374000 2 connected
5e35d4fc44ff1389eaa13b06547a4ae7b7391040 192.168.66.53:6381@16381 master - 0 1649589373000 2 connected 10922-16383

3. Redis Cluster 集群验证

3.1. 尝试插入数据

# 1. 尝试插入数据
~]# redis-cli -h 192.168.66.51 -p 6381
192.168.66.51:6381> set  k1 v1
(error) MOVED 12706 192.168.66.53:6381
192.168.66.51:6381> set  k2 v2
OK
192.168.66.51:6381> set  k3 v3
OK
192.168.66.51:6381> set  k4 v4
(error) MOVED 8455 192.168.66.52:6381
192.168.66.51:6381> set  k5 v5
(error) MOVED 12582 192.168.66.53:6381

# 2. 根据错误提示登录到 Redis 节点
~]# redis-cli -h 192.168.66.52 -p 6381
192.168.66.52:6381> set k4 v4
OK

目前的现象:

  1. 在 db01 的 6381 节点上插入数据提示报错
  2. 根据报错信息登录到指定的 Redis 节点上,执行插入命令成功
  3. 在 db01 的 6381 节点上插入数据,有的时候可以有的时候不可以

如何解决上面现象呢?使用 -c 参数即可。

~]# redis-cli -c -h 192.168.66.51 -p 6381
192.168.66.51:6381> set k1 v1
-> Redirected to slot [12706] located at 192.168.66.53:6381
OK
192.168.66.53:6381> set k2 v2
-> Redirected to slot [449] located at 192.168.66.51:6381
OK
192.168.66.51:6381> set k3 v3
OK
192.168.66.51:6381> set k4 v4
-> Redirected to slot [8455] located at 192.168.66.52:6381
OK

3.2. 问题原因

因为 Redis Cluster 集群模式有ASK路由规则。加入 -c 参数后,会自动跳转到目标节点出来,并且有目标节点返回信息

redis查看集群信息命令 redis查看集群状态_学习_04

3.3. 验证集群是否足够足迹足够平均

Cluster 架构是分布式,创建的 key 会通过 HASH 将数据平均的分布在每个节点的槽位上

# 1. 插入 10000 条数据,查看节点上的数据是否均分
for i in {1..10000};do redis-cli -c -h 192.168.66.51 -p 6381 set k_${i} v_${i};echo ${i};done

# 2. 查看每个节点上的数据量,可以看到足够均
~]# redis-cli -c -h 192.168.66.51 -p 6381 DBSIZE
(integer) 3345
~]# redis-cli -c -h 192.168.66.52 -p 6381 DBSIZE
(integer) 3315
~]# redis-cli -c -h 192.168.66.53 -p 6381 DBSIZE
(integer) 3344

# 3. 验证是否足够随机
redis-cli -c -h 192.168.66.51 -p 6381 keys \* > keys_all.txt
cat keys_all.txt |awk -F "_" '{print $2}'|sort -rn

# 4. 允许节点的 key 在 2% 误差的依据来源
~]# redis-cli --cluster rebalance 192.168.66.51 6381
>>> Performing Cluster Check (using node 192.168.66.51:6381)
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
*** No rebalancing needed! All nodes are within the 2.00% threshold.

3.4. 检查集群健康状态

~]# redis-cli --cluster info 192.168.66.51 6381
192.168.66.51:6381 (4905ba18...) -> 3345 keys | 5461 slots | 1 slaves.
192.168.66.52:6381 (98ac4f57...) -> 3314 keys | 5461 slots | 1 slaves.
192.168.66.53:6381 (5e35d4fc...) -> 3344 keys | 5462 slots | 1 slaves.
[OK] 10003 keys in 3 masters.
0.61 keys per slot on average.

3.5. Redis Cluster 集群的命令帮助

~]# redis-cli --cluster help
Cluster Manager Commands:
  create         host1:port1 ... hostN:portN
                 --cluster-replicas <arg>
  check          host:port
                 --cluster-search-multiple-owners
  info           host:port
  fix            host:port
                 --cluster-search-multiple-owners
  reshard        host:port
                 --cluster-from <arg>
                 --cluster-to <arg>
                 --cluster-slots <arg>
                 --cluster-yes
                 --cluster-timeout <arg>
                 --cluster-pipeline <arg>
                 --cluster-replace
  rebalance      host:port
                 --cluster-weight <node1=w1...nodeN=wN>
                 --cluster-use-empty-masters
                 --cluster-timeout <arg>
                 --cluster-simulate
                 --cluster-pipeline <arg>
                 --cluster-threshold <arg>
                 --cluster-replace
  add-node       new_host:new_port existing_host:existing_port
                 --cluster-slave
                 --cluster-master-id <arg>
  del-node       host:port node_id
  call           host:port command arg arg .. arg
  set-timeout    host:port milliseconds
  import         host:port
                 --cluster-from <arg>
                 --cluster-copy
                 --cluster-replace
  help

For check, fix, reshard, del-node, set-timeout you can specify the host and port of any working node in the cluster.

4. 模拟故障转移

三主三从架构允许最多坏掉 1 台主机,模拟将 db01 主机的主库挂掉,查看集群见得故障转移

模拟思路:

  1. 将 db01 的 6381 主库关掉,查看集群状态信息是否将slave自动切换为 master
  2. 当 master 重新上线后将会变成一个节点的从库
  3. 将 master 通过 cluster failover 重新成为主库

4.1. 模拟 db01:6381 坏掉

# 1. 挂掉 db01:6381
~]# systemctl stop redis-master.service

# 2. 查看 db01:6381 的 db02:6382 从库的日志
# 先是由于主库挂了状态变成fail,当从库变成主库后,状态再次变为ok
~]# tail /opt/redis/6382/logs/6382.log
~]# tail -11 /opt/redis/6382/logs/6382.log
5938:S 10 Apr 2022 19:23:24.708 # Cluster state changed: fail
5938:S 10 Apr 2022 19:23:24.808 # Start of election delayed for 935 milliseconds (rank #0, offset 123705).
5938:S 10 Apr 2022 19:23:25.718 * Connecting to MASTER 192.168.66.51:6381
5938:S 10 Apr 2022 19:23:25.718 * MASTER <-> REPLICA sync started
5938:S 10 Apr 2022 19:23:25.718 # Error condition on socket for SYNC: Connection refused
5938:S 10 Apr 2022 19:23:25.819 # Starting a failover election for epoch 6.
5938:S 10 Apr 2022 19:23:25.822 # Failover election won: I'm the new master.
5938:S 10 Apr 2022 19:23:25.822 # configEpoch set to 6 after successful failover
5938:M 10 Apr 2022 19:23:25.822 # Setting secondary replication ID to 6ca3c30a6bd82caff9dd74472d34fe667c28f545, valid up to offset: 123706. New replication ID is be4be5d6195107adc4016fb97a9c30327e32310d
5938:M 10 Apr 2022 19:23:25.822 * Discarding previously cached master state.
5938:M 10 Apr 2022 19:23:25.822 # Cluster state changed: ok

# 3. 查看集群信息
~]# redis-cli -h 192.168.66.51 -p 6382 CLUSTER NODES
f8ee3d4c4019f9a03e56cd913b2c3881f41b5586 192.168.66.51:6382@16382 myself,slave 5e35d4fc44ff1389eaa13b06547a4ae7b7391040 0 1649590278000 0 connected
4905ba18001a92c96e08b6759e5fe374ea6f03d8 192.168.66.51:6381@16381 master,fail - 1649589789034 1649589785108 1 disconnected
5e35d4fc44ff1389eaa13b06547a4ae7b7391040 192.168.66.53:6381@16381 master - 0 1649590277748 2 connected 10922-16383
042c2048aa5a05821f9cc2a06618e5c8c63a6966 192.168.66.53:6382@16382 slave 98ac4f577aaaff768c65116f64da76f3bf3eaaa7 0 1649590278753 5 connected
c832842e805bfc7f62e9789d7d2e1f1d78798315 192.168.66.52:6382@16382 master - 0 1649590277000 6 connected 0-5460
98ac4f577aaaff768c65116f64da76f3bf3eaaa7 192.168.66.52:6381@16381 master - 0 1649590279760 5 connected 5461-10921

# 4. 查看集群状态
~]# redis-cli -h 192.168.66.51 -p 6382 CLUSTER INFO
cluster_state:ok
cluster_slots_assigned:16384
cluster_slots_ok:16384
cluster_slots_pfail:0
cluster_slots_fail:0
cluster_known_nodes:6
cluster_size:3
cluster_current_epoch:6
cluster_my_epoch:2
cluster_stats_messages_ping_sent:6384
cluster_stats_messages_pong_sent:1357
cluster_stats_messages_meet_sent:9
cluster_stats_messages_sent:7750
cluster_stats_messages_ping_received:1348
cluster_stats_messages_pong_received:1303
cluster_stats_messages_meet_received:9
cluster_stats_messages_fail_received:2
cluster_stats_messages_auth-req_received:1
cluster_stats_messages_received:2663

# 5. 验证集群是否可用
~]# redis-cli -c -h 192.168.66.51 -p 6382 set k1 v1
OK

当主库挂掉后,查看集群信息时会看到提示主库已经 fail 状态,此时可用看到 192.168.66.52 的 6382 节点成为了 master,192.168.66.52 的 6382 节点是 192.168.66.51:6381 的从库,从库变为主库后,集群状态再次变为 ok

4.2. db01 节点的主库恢复前的架构图

当主库重新加入到集群后,架构图就会变成如下的样子,主库的 192.168.66.51:6381 就变成了 192.168.66.52:6382 的从库

redis查看集群信息命令 redis查看集群状态_Redis_05

# 1. 启动 192.168.66.51 的 6381 实例
~]# systemctl start redis-master.service
~]# netstat -nlutp|grep 6381
tcp        0      0 192.168.66.51:16381     0.0.0.0:*               LISTEN      14808/redis-server
tcp        0      0 192.168.66.51:6381      0.0.0.0:*               LISTEN      14808/redis-server

# 2. 查看集群信息,可以看到 192.168.66.52 的 6382 变成了主库,192.168.66.51 的 6381 变成了从库
~]# redis-cli -c -h 192.168.66.51 -p 6382 CLUSTER NODES
f8ee3d4c4019f9a03e56cd913b2c3881f41b5586 192.168.66.51:6382@16382 myself,slave 5e35d4fc44ff1389eaa13b06547a4ae7b7391040 0 1649590460000 0 connected
4905ba18001a92c96e08b6759e5fe374ea6f03d8 192.168.66.51:6381@16381 slave c832842e805bfc7f62e9789d7d2e1f1d78798315 0 1649590462033 6 connected
5e35d4fc44ff1389eaa13b06547a4ae7b7391040 192.168.66.53:6381@16381 master - 0 1649590460021 2 connected 10922-16383
042c2048aa5a05821f9cc2a06618e5c8c63a6966 192.168.66.53:6382@16382 slave 98ac4f577aaaff768c65116f64da76f3bf3eaaa7 0 1649590458010 5 connected
c832842e805bfc7f62e9789d7d2e1f1d78798315 192.168.66.52:6382@16382 master - 0 1649590461028 6 connected 0-5460
98ac4f577aaaff768c65116f64da76f3bf3eaaa7 192.168.66.52:6381@16381 master - 0 1649590460000 5 connected 5461-10921

4.3. 将恢复的故障节点重新变成主

目前主库已经从新上线了,且现在是 192.168.66.52:6382 的从库,而原来的 192.168.66.52:6382 从库变成了 192.168.66.51:6381 的主库,我们需要把关系切换回来,不能让一台主机上面有两个主库,每次故障处理后一定要把架构修改回原来的样子。

从库切换成主库也特别简单,只需要执行一个 CLUSTER FAILOVER 即可变为主库

CLUSTER FAILOVER 原理:FAILOVER 原理也就是先执行了 slaveof no one,然后在对应得主库变为从库的集群上执行了 slaveof

# 1. 将故障上线的主库重新成为主库
 ~]# redis-cli -c -h 192.168.66.51 -p 6381 CLUSTER FAILOVER
OK

# 2. 查看集群信息,可以看到 192.168.66.51 的 6381 变成了 master,而 192.168.66.52 的 6382 变成了 slave
~]# redis-cli -c -h 192.168.66.51 -p 6381 CLUSTER NODES
c832842e805bfc7f62e9789d7d2e1f1d78798315 192.168.66.52:6382@16382 slave 4905ba18001a92c96e08b6759e5fe374ea6f03d8 0 1649590581874 7 connected
98ac4f577aaaff768c65116f64da76f3bf3eaaa7 192.168.66.52:6381@16381 master - 0 1649590580867 5 connected 5461-10921
5e35d4fc44ff1389eaa13b06547a4ae7b7391040 192.168.66.53:6381@16381 master - 0 1649590580000 2 connected 10922-16383
4905ba18001a92c96e08b6759e5fe374ea6f03d8 192.168.66.51:6381@16381 myself,master - 0 1649590581000 7 connected 0-5460
f8ee3d4c4019f9a03e56cd913b2c3881f41b5586 192.168.66.51:6382@16382 slave 5e35d4fc44ff1389eaa13b06547a4ae7b7391040 0 1649590580000 2 connected
042c2048aa5a05821f9cc2a06618e5c8c63a6966 192.168.66.53:6382@16382 slave 98ac4f577aaaff768c65116f64da76f3bf3eaaa7 0 1649590579859 5 connected

到此 cluster 集群故障转移成功,集群状态一切正常

~]# redis-cli --cluster info 192.168.66.51 6381
192.168.66.51:6381 (4905ba18...) -> 3345 keys | 5461 slots | 1 slaves.
192.168.66.52:6381 (98ac4f57...) -> 3314 keys | 5461 slots | 1 slaves.
192.168.66.53:6381 (5e35d4fc...) -> 3344 keys | 5462 slots | 1 slaves.
[OK] 10003 keys in 3 masters.
0.61 keys per slot on average.

5. Redis Cluster 需要注意

生产环境数据量可能非常大,当主库故障重新上线是,执行 CLUSTER FAILOVER 会非常慢,因为这个就相当于是主从复制切换了,从库(刚上线的原架构主库)关闭主从复制,主库(原架构中主库的从库)同步从库(刚上线的原架构主库),然后从库(刚上线的原架构主库)重新变为主库,这个时间一定要等,切记前往不要应为慢在主库(原架构中主库的从库)上同步手动进行 CLUSTER REPLICATE,这样确实非常快的将主库(原架构中主库的从库)从新变成从库,但是这也就意味着数据全部丢失,因为 cluster replicate 相当于 slaveof。slaveof 会把自己的主库清理掉,这时候 从库(刚上线的原架构主库)再在执行这 CLUSTER FAILOVER 同步着主库(原架构中主库的从库)的数据,主库那边执行了 replicate 去同步从库(刚上线的原架构主库),从而导致从库(刚上线的原架构主库)还没有同步完主库(原架构中主库的从库),主库(原架构中主库的从库)数据就丢失,整个集群还是可以用的,只是这个主库节点和从节点数据全部丢失,其他两个主库从库还能使用。

切记,当从库执行CLUSTER FAILOVER变为主库时,一定不要在主库上执行CLUSTER REPLICATE变为从库,虽然CLUSTER REPLICATE变为从库很快,但是会清空自己的数据去同步主库,这时主库还没有数据,因此就会导致数据全部丢失

CLUSTER FAILOVER:首先执行slave on one变为一个单独的节点,然后在要变成从库的节点上执行slaveof,只要从库执行完slave of,执行CLUSTER FAILOVER的节点就变成了主库

CLUSTER REPLICATE:只是执行了slaveof使自身成为从节点

当redis cluster主从正在同步时,不要执行cluster replicate,当主从复制完在执行,如何看主从是否复制完就要看节点的rdb文件是否是.tmp结尾的,如果是tmp结尾就说明他们正在同步数据,此时不要对集群做切换操作

6. Redis Cluster 小结

  1. 3.x 版本以后推出集群功能
  2. cluster 集群有 16384 个槽位,误差在 2% 之间
  3. 槽位与序号顺序无关,重点是槽的数量
  4. 通过发现集群,与集群之间实现消息传递
  5. 配置文件无需手动修改,都是自动生成的
  6. 分配操作,必须将所有槽位分配完毕
  7. 清理复制关系,画架构图,按照架构图执行复制命令
  8. 当集群状态为 ok 时,集群才可以正常使用
  9. 反复测试,批量插入 key,验证分配是否均匀
  10. 测试高可用,关闭任意主节点,集群是否自动迁移
  11. 当主节点修复后,执行主从关系切换
  12. 做实验尽量贴近生产环境,尽量使用和生产环境一样数量的数据
  13. 评估和计量同步数据、故障转移完成的时间
  14. 向领导汇报时要有图、文档、实验环境,随时都可以演示

当应用需要连接 Redis Cluster 集群时要将所有节点都写在配置文件中

# redis配置文件 redis.properties
# 代表redis多个节点的ip与端口号,多个节点需要使用“,”隔开。
spring.redis.cluster.nodes=192.168.0.108:8001,192.168.0.108:8002,192.168.0.108:8003,192.168.0.108:8004,192.168.0.108:8005,192.168.0.108:8006
# 最大的要重定向的次数(由于集群中数据存储在多个节点所以,在访问数据时需要通过节点进行转发)
# 连接超时的时间
spring.redis.cluster.timeout=5000
# 最大的连接重试次数
spring.redis.cluster.max-attempts=3
# 读取数据超时
spring.redis.cluster.soTimeout=3000
spring.redis.cluster.max-redirects=3