Redis集群节点管理(cluster)

【目标】

  1. 掌握集群优势
  2. 掌握集群搭建
  3. 集群原理

【理论知识】

  1. 集群概念与优缺点
  2. 数据分区算法
  3. redis-benchmark 命令
  4. 集群分片算法

【实际操作】

  1. 集群搭建与配置
  2. 集群环境测试
  3. 性能测试

节点与插槽管理

首先准备一个新的节点,添加配置问件。

vim /usr/local/redis/cluster/conf/redis-6377.conf
# 放行访问IP限制
bind 0.0.0.0
# 端口
port 6377
# 后台启动
daemonize yes

# 日志存储目录及日志文件名
logfile "/usr/local/redis/cluster/log/redis-6377.log"
# rdb数据文件名
dbfilename dump-6377.rdb

# aof模式开启和aof数据文件名
appendonly yes
appendfilename "appendonly-6377.aof"

# rdb数据文件和aof数据文件的存储目录
dir /usr/local/redis/cluster/data

# 设置密码
requirepass 123456
# 从节点访问主节点密码(必须与 requirepass 一致)
masterauth 123456

# 是否开启集群模式,默认 no
cluster-enabled yes
# 集群节点信息文件,会保存在 dir 配置对应⽬录下
cluster-config-file nodes-6377.conf

# 集群节点连接超时时间
cluster-node-timeout 15000
# 集群节点 IP
cluster-announce-ip 192.168.10.103

# 集群节点映射端口
cluster-announce-port 6377
# 集群节点总线端口
cluster-announce-bus-port 16377

启动新的节点。

/usr/local/redis/bin/redis-server /usr/local/redis/cluster/conf/redis-6377.conf

添加主节点

使用以下命令添加主节点。

redis-cli --cluster add-node new_host:new_port existing_host:existing_port --cluster-master-id node_id

例子

/usr/local/redis/bin/redis-cli -a 123456 --cluster add-node 192.168.10.103:6377 192.168.10.103:6375 --cluster-master-id 4568a2560a688898a5d2337bce3a288f12355ae8
  • **new_host:new_port:**为要新添加的主节点 IP 和端口
  • **existing_host:existing_port:**表示的是环境中已存在的最后一个主节点的 IP 和端口,这个可以通过 查看节点信息得知,根据 slots 槽数,192.168.10.103:6375 对应的节点槽数是 10923-16383, 16383 表示的是最后的槽数
  • **–cluster-master-id:**表示的是最后一个主节点的节点 ID,表示的是新添加的主节点要在这个节点 后面

在这里插入图片描述

# 再次查看集群信息
/usr/local/redis/bin/redis-cli -c -a 123456 -h 192.168.10.103 -p 6376 cluster nodes

会发现 6377 端口对应的节点已经加口到集群中,是主节点,但是没有从节点,也没有分配槽数。

在这里插入图片描述

重新分片

​ 添加完新节点后,需要对新添加的主节点进行 hash 槽重新分配,这样该主节点才能存储数据,Redis 共有16384个槽。

redis-cli --cluster reshard host:port --cluster-from node_id --cluster-to node_id --cluster-slots <args> --cluster-yes

例子

 /usr/local/redis/bin/redis-cli -a 123456 --cluster reshard 192.168.10.103:6377 --cluster-from a1cd39d24bd2c9456026d592f0fac8728cea0e29 --cluster-to 66f3cbc063a84ff3c209fa2db7104e4666d82e9c --cluster-slots 2000
  • **host:port:**集群中随便一个节点的 IP : PORT 连接集群用的
  • **–cluster-from node_id:**表示的是从哪个节点取出槽,节点 ID
  • **–cluster-to node_id:**表示的是取出的槽添加给哪个节点,也就是新添加的那个主节点 ID
  • **–cluster-slots 2000:**表示的是给新主节点分配多少,此处 2000 表示是分配从 0-1999 个 slots 槽数,然后需要输入 yes 重新进行槽分配。
  • **–cluster-yes:**不回显槽分配信息直接移动。

在这里插入图片描述

# 再次查看集群信息
/usr/local/redis/bin/redis-cli -c -a 123456 -h 192.168.10.103 -p 6376 cluster nodes
# 会发现 6377 端⼝对应的主节点已经有 slots 槽数了,并且是从 0 开始的

在这里插入图片描述

添加从节点

准备节点

首先准备一个新的节点,添加配置文件。

vim /usr/local/redis/cluster/conf/redis-6378.conf
# 放行访问IP限制
bind 0.0.0.0
# 端口
port 6378
# 后台启动
daemonize yes
# 日志存储目录及日志文件名
logfile "/usr/local/redis/cluster/log/redis-6378.log"
# rdb数据文件名
dbfilename dump-6378.rdb
# aof模式开启和aof数据文件名
appendonly yes
appendfilename "appendonly-6378.aof"
# rdb数据文件和aof数据文件的存储目录
dir /usr/local/redis/cluster/data
# 设置密码
requirepass 123456
# 从节点访问主节点密码(必须与 requirepass 一致)
masterauth 123456
# 是否开启集群模式,默认 no
cluster-enabled yes
# 集群节点信息文件,会保存在 dir 配置对应目录下
cluster-config-file nodes-6378.conf
# 集群节点连接超时时间
cluster-node-timeout 15000
# 集群节点 IP
cluster-announce-ip 192.168.10.103
# 集群节点映射端口
cluster-announce-port 6378
# 集群节点总线端口
cluster-announce-bus-port 16378
启动新的节点。
/usr/local/redis/bin/redis-server /usr/local/redis/cluster/conf/redis-6378.conf
添加从节点

使用以下命令添加从节点。

redis-cli --cluster add-node new_host:new_port existing_host:existing_port --cluster-slave --cluster-master-id node_id

例子

/usr/local/redis/bin/redis-cli -a 123456 --cluster add-node 192.168.10.103:6378 192.168.10.103:6377 --cluster-slave --cluster-master-id 66f3cbc063a84ff3c209fa2db7104e4666d82e9c
  • **new_host:new_port:**表示的是要添加的那个从节点的 IP 和端口
  • **existing_host:existing_port:**表示的是要给哪个主节点添加从节点
  • **–cluster-slave:**表示的是要添加从节点
  • **–cluster-master-id node_id:**表示要给哪个主节点添加从节点,该主节点节点 ID

在这里插入图片描述

# 再次查看集群信息
/usr/local/redis/bin/redis-cli -c -a 123456 -h 192.168.10.103 -p 6376 cluster nodes
# 会发现 6378 端口对应的节点已经是 6377 端口对应的从节点

在这里插入图片描述

删除从节点

redis-cli --cluster del-node host:port node_id

例子

/usr/local/redis/bin/redis-cli -a 123456 --cluster del-node 192.168.10.103:6378 5800786da604cedb56eb1b50d83e9f82bb09b421
  • **host:port:**表示的是要删除的那个节点的 IP 和端口
  • **node_id:**表示的是删除的那个节点的节点 ID

在这里插入图片描述

# 再次查看集群信息
/usr/local/redis/bin/redis-cli -c -a 123456 -h 192.168.10.103 -p 6376 cluster nodes
# 环境中 6378 从节点已被移除

在这里插入图片描述

删除主节点

​ 删除主节点稍微麻烦一点,因为主节点分配了 slots 槽,所以必须先把 slots 槽放到其他可用节点中去, 然后再进行移除节点操作才行,不然会出现数据丢失问题。

重新分片

把数据移动到其它主节点中去,执行重新分片命令。

/usr/local/redis/bin/redis-cli -a 123456 --cluster reshard 192.168.10.103:6377

192.168.10.103:6377 分配了 2000 个槽,这里输入 2000 即可。

在这里插入图片描述

回车以后,出现 what is the receiving node ID? 意思是你想移动到那个节点上。

我想移动到 6371 的节点上,那么此处输入 6371 节点的 ID。

在这里插入图片描述

回车以后,需要填写数据源节点 ID,就是 6377 节点的 ID,因为我们要把 6377 节点的数据移动到其他 节点去。

在这里插入图片描述

​ 回车以后,还可以继续选择其他源节点,但是我这里只想把 6377 节点分到其它地方就行,此处输入 done 即可,否则输入其它节点的 ID,最后输入 done。

在这里插入图片描述

最后输入 yes 等待转移结束。

在这里插入图片描述

# 再次查看集群信息
/usr/local/redis/bin/redis-cli -c -a 123456 -h 192.168.10.103 -p 6376 cluster nodes
# 发现 6377 已经没有 slots 槽分配了

在这里插入图片描述

删除节点

接下来,调用删除从节点的方式,删除主节点。

/usr/local/redis/bin/redis-cli -a 123456 --cluster del-node 192.168.10.103:6377 66f3cbc063a84ff3c209fa2db7104e4666d82e9c

在这里插入图片描述

# 再次查看集群信息
/usr/local/redis/bin/redis-cli -c -a 123456 -h 192.168.10.103 -p 6376 cluster nodes
# 环境中 6377 主节点已被移除

在这里插入图片描述

MOVED转向

当我们使用操作 Redis 单节点的 Client 来操作集群时,比如使用以下方式登入客户端。

/usr/local/redis/bin/redis-cli -a 123456 -h 192.168.10.103 -p 6376

在这里插入图片描述

​ 常常能够遇到上面的报错。按照 Redis 官方规范,一个 Redis 客户端可以向集群中的任意节点(包括从节点)发送命令请求。节点会对命令请求进行分析,如果该命令是集群可以执行的命令,那么节点会查找 这个命令所要处理的键所在的槽。如果处理该命令的槽位于当前节点,那么命令可以顺利执行,否则当前 节点会返回 MOVED 错误,让客户端到另一个节点执行该命令。

​ Redis 官方规范要求所有客户端都应处理 MOVED 错误,从而实现对用户的透明。我们上面看到的错误就 是 MOVED 错误。表示执行该命令所需要的 slot 是 14315 号哈希槽,负责该槽的节点是 192.168.10.103:637

单机模式/集群模式下 MOVED 错误的显示区别。

  • 集群模式的 redis-cli 在接到 MOVED 错误时不会打印错误,而是自动根据错误提供的 IP 地址和端口 进行转向动作
  • 单机模式的 redis-cli 客户端会打印 MOVED 错误,客户端需要做出处理

Redis 的哲学

​ 为什么服务端不对客户端的请求作出处理?因为 Redis 的哲学:保持 Server 端的尽量简洁,能不在 Server 端做的事情都不在 Server 端做。

解决方案

客户端使用集群模式连接

启动时使用 -c 参数来启动集群模式

/usr/local/redis/bin/redis-cli -c -a 123456 -h 192.168.10.103 -p 6376

如果是第三方客户端也使用集群方式进行连接。

客户端优化

对于不支持集群模式连接的客户端,可以通过以下方式进行解决。

​ 节点在 MOVED 错误中会直接返回目标节点的 IP 和端口号。客户端可以记录槽 14315 由节点 192.168.10.103:6375 负责处理这一信息, 这样当再次有命令需要对槽 14315 执行时, 客户端就可以加 快寻找正确节点的速度。这样,当集群处于稳定状态时,所有客户端最终都会保存有一个哈希槽至节点的 映射记录,使得集群非常高效: 客户端可以直接向正确的节点发送命令请求,无须转向、代理或者其他任何可能发生单点故障(single point failure)的实体(entiy)。

ASK转向

除了 MOVED 转向,Redis 规范还要求客户端实现对 ASK 转向的处理。

​ 在进行重新分片期间,源节点向目标节点迁移一个槽的过程中,可能会出现这样一种情况:被迁移槽的一部分键值对保存在源节点里面,而另一部分键值对则保存在目标节点里面。

当客户端向源节点发送一个命令,并且命令要处理的键恰好就属于正在被迁移的槽时:

  • 源节点会先在自己的数据里面查找指定的键,如果找到的话,就直接执行客户端发送的命令
  • 相反地,如果源节点没能在自己的数据里面找到指定的键,那么这个键有可能已经 被迁移到了目标节点,源节点将向客户端返回一个 ASK 错误,指引客户端转向正在导入槽的目标节点,并再次发送之前 想要执行的命令

单机模式/集群模式下 ASK 错误的显示区别

  • 集群模式的 redis-cli 在接到 ASK 错误时不会打印错误,而是自动根据错误提供的 IP 地址和端口进行转向动作
  • 单机模式的 redis-cli 客户端会打印 ASK 错误,客户端需要做出处理

解决方案

客户端使用集群模式连接

启动时使用 -c 参数来启动集群模式。

/usr/local/redis/bin/redis-cli -c -a 123456 -h 192.168.10.103 -p 6376

如果是第三方客户端也使用集群方式进行连接。

ASKING命令

ASKING命令功能:打开发送该命令的客户端的 REDIS_ASKING 标识。

​ 我们可以使用普通模式的 redis-cli 客户端,向正在导入某个槽的节点先发送 ASKING 命令,然后其他命 令例如 GET 就会被正在导入槽的目标节点执行。

192.168.10.103:6376> ASKING # 打开 REDIS_ASKING 标识
OK
192.168.10.103:6376> get username # 移除 REDIS_ASKING 标识
"zhangsan"
192.168.10.103:6376> get username # REDIS_ASKING 标识未打开,执行失败

​ 注意:客户端的 REDIS_ASKING 标识是一个一次性标识,当节点执行了一个带有 REDIS_ASKING 标识 的客户端发送的命令之后,客户端的 REDIS_ASKING 标识就会被移除。

ASK错误和MOVED错误的区别

ASK 错误和 MOVED 错误都会导致客户端转向,它们的区别在于:

​ MOVED 错误代表槽的负责权已经从一个节点转移到了另一个节点。当节点需要让一个客户端长期地 (permanently)将针对某个槽的命令请求发送至另一个节点时,节点向客户端返回 MOVED 转向。

​ ASK 错误只是两个节点在迁移槽的过程中使用的一种临时措施。当节点需要让客户端仅仅在下一个命令请求中转向至另一个节点时,节点向客户端返回 ASK 转向。客户端是不能直接请求 ASK 转向的目标机器的,而是必须先发送一个 ASKING 命令。

自动故障转移

启动 6 个 Redis 节点

/usr/local/redis/bin/redis-server /usr/local/redis/cluster/conf/redis-6371.conf
2 /usr/local/redis/bin/redis-server /usr/local/redis/cluster/conf/redis-6372.conf
3 /usr/local/redis/bin/redis-server /usr/local/redis/cluster/conf/redis-6373.conf
4 /usr/local/redis/bin/redis-server /usr/local/redis/cluster/conf/redis-6374.conf
5 /usr/local/redis/bin/redis-server /usr/local/redis/cluster/conf/redis-6375.conf
6 /usr/local/redis/bin/redis-server /usr/local/redis/cluster/conf/redis-6376.conf

查看集群状态

/usr/local/redis/bin/redis-cli -c -a 123456 -h 192.168.10.101 -p 6372 cluster nodes

我们使用 6371 <> 6374 这对主从节点给大家演示故障转移。

d3028e6da602735c18f5917102a818daaf4f5a8a 192.168.10.103:6376@16376slave 339e0d0b6f92322fb269a83c0b43c44850e7e0aa 0 1607409600000 3 connected

afe0b3936c837a46972055c7b9b691bdd8149b7d 192.168.10.101:6371@16371 myself,master - 0 1607409603000 1 connected 0-5460

339e0d0b6f92322fb269a83c0b43c44850e7e0aa 192.168.10.102:6373@16373 master - 0 1607409603857 3 connected 5461-10922

b444cc7e6a6ee077e2d2292c68abde22977c7e5d 192.168.10.102:6374@16374 slave afe0b3936c837a46972055c7b9b691bdd8149b7d 0 1607409603000 1 connected

70613e4ec9d16162808accd9e5e2da0792c9ec12 192.168.10.101:6372@16372 slave f3353d11eae2dae1e46cdee9134beea872d1c6e8 0 1607409604000 5 connected

f3353d11eae2dae1e46cdee9134beea872d1c6e8 192.168.10.103:6375@16375 master - 0 1607409604868 5 connected 10923-16383

模拟主节点故障

# 查看 6371 节点的 pid 进程号
/usr/local/redis/bin/redis-cli -a 123456 -h 192.168.10.101 -p 6371 info server | grep process_id
# 杀死进程
kill -9 进程号

查看从节点日志

tail -f -n 1000 /usr/local/redis/cluster/log/redis-6374.log
# 与主节点失去联系
1181:S 2020 15:00:02.044 # Connection with master lost.
1181:S * Caching the disconnected master state.

# (集群节点连接超时时间由 cluster-node-timeout 15000 决定)
# 连接主节点 192.168.10.101:6371
1181:S * Connecting to MASTER 192.168.10.101:6371
# 主从同步开始
1181:S * MASTER <-> REPLICA sync started

# SYNC 出错:拒绝连接
1181:S # Error condition on socket for SYNC: Connection refused
1181:S * Connecting to MASTER 192.168.10.101:6371
1181:S * MASTER <-> REPLICA sync started
1181:S # Error condition on socket for SYNC: Connection refused
1181:S * Connecting to MASTER 192.168.10.101:6371
1181:S * MASTER <-> REPLICA sync started
.
.
.
1181:S # Error condition on socket for SYNC: Connection refused
1181:S * Connecting to MASTER 192.168.10.101:6371
1181:S * MASTER <-> REPLICA sync started
1181:S # Error condition on socket for SYNC: Connection refused

# 将节点 ae0b3936c837a46972055c7b9b691bdd8149b7d 标记为失败(达到法定比数)
1181:S 2020 15:00:20.561 * Marking node afe0b3936c837a46972055c7b9b691bdd8149b7d as failing (quorum reached).
# 集群状态改为:失败
1181:S # Cluster state changed: fail
# 选举开始
1181:S # Start of election delayed for 595 milliseconds (rank #0,offset 1624).
1181:S * Connecting to MASTER 192.168.10.101:6371
1181:S * MASTER <-> REPLICA sync started
1181:S # Error condition on socket for SYNC: Connection refused
# 启动第7次纪元的故障切换选举
1181:S # Starting a failover election for epoch 7.
# 选举成功,自己(192.168.10.102:6374)是新主节点
1181:S # Failover election won: I'm the new master.
# 故障切换成功后,配置文件 config epoch 设置为 7
1181:S # configEpoch set to 7 after successful failover
# 抛弃之前缓存的主节点状态
1181:M * Discarding previously cached master state.
# 将二级主从 ID 设置为 xxx 有效偏移量为 1625 新的主从 ID 为 xxx
1181:M # Setting secondary replication ID to 0b35768c25c0a463cb73b9056dc832897a815ba7, valid up to offset: 1625. New replication ID is 9668c0e62ba28defda2b0360daa6b7f0fe5f89e0
# 集群状态 OK
1181:M # Cluster state changed: ok

查看集群状态

/usr/local/redis/bin/redis-cli -c -a 123456 -h 192.168.10.101 -p 6372 cluster nodes

发现 6371 主节点显示 fail 状态,原来的 6374 从节点已经升级为主节点。

在这里插入图片描述

启动原主节点

/usr/local/redis/bin/redis-server /usr/local/redis/cluster/conf/redis-6371.conf

查看集群状态

/usr/local/redis/bin/redis-cli -c -a 123456 -h 192.168.10.101 -p 6372 cluster nodes

查看主从日志

原主节点现在是从节点

tail -f -n 1000 /usr/local/redis/cluster/log/redis-6371.log
1265:C # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
1265:C # Redis version=6.0.9, bits=64, commit=00000000, modified=0, pid=1265, just started
1265:C # Configuration loaded
1265:M * Increased maximum number of open files to 10032 (it wasoriginally set to 1024).
1265:M * Node configuration loaded, I'm afe0b3936c837a46972055c7b9b691bdd8149b7d
1265:M * Running mode=cluster, port=6371.

1265:M # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128.
1265:M # Server initialized

1265:M # WARNING overcommit_memory is set to 0! Background save may fail under low memory condition. To fix this issue add 'vm.overcommit_memory = 1' to /etc/sysctl.conf and then reboot or run the command 'sysctl vm.overcommit_memory=1' for this to take effect.

1265:M # WARNING you have Transparent Huge Pages (THP) support enabled in your kernel. This will create latency and memory usage issues with Redis. To fix this issue run the command 'echo madvise> /sys/kernel/mm/transparent_hugepage/enabled' as root, and add it to your /etc/rc.local in order to retain the setting after a reboot. Redis must be restarted after THP is disabled (set to 'madvise' or 'never').

1265:M * DB loaded from append only file: 0.462 seconds
1265:M * Ready to accept connections

# 检测到配置变更。将自己重新配置为 b444cc7e6a6ee077e2d2292c68abde22977c7e5d 的副本
1265:M # Configuration change detected. Reconfiguring myself as areplica of b444cc7e6a6ee077e2d2292c68abde22977c7e5d

# 变为副本前的准备工作
1265:S * Before turning into a replica, using my own master parameters to synthesize a cached master: I may be able to synchronizewith the new master with just a partial transfer.
1265:S # Cluster state changed: ok

# 连接主节点 192.168.10.102:6374
1265:S * Connecting to MASTER 192.168.10.102:6374
# 主从同步开始
1265:S * MASTER <-> REPLICA sync started
# 触发 SYNC 的非阻塞连接事件
1265:S * Non blocking connect for SYNC fired the event.
# 主节点已回复 PING 命令,同步可以继续
1265:S * Master replied to PING, replication can continue...
# 尝试部分重同步
1265:S * Trying a partial resynchronization (request e17197fbbd478907a28c62aea61f7d1486f5adf:1).
# 全量复制主节点
1265:S * Full resync from master: 9668c0e62ba28defda2b0360daa6b7f0fe5f89e0:1624
# 抛弃之前缓存的主节点状态
1265:S * Discarding previously cached master state.
# 主从复制:从主节点接收 4640673 个字节存储到磁盘
1265:S * MASTER <-> REPLICA sync: receiving 4640673 bytes from master to disk
# 主从复制:刷新旧数据
1265:S * MASTER <-> REPLICA sync: Flushing old data
# 主从复制:将数据加载到内存
1265:S * MASTER <-> REPLICA sync: Loading DB in memory

# 加载 RDB
1265:S * Loading RDB produced by version 6.0.9
1265:S * RDB age 1 seconds
1265:S * RDB memory usage when created 27.06 Mb

# 主从复制:顺利完成
1265:S * MASTER <-> REPLICA sync: Finished with success

# 后台开启进程执行 AOF 写入
1265:S * Background append only file rewriting started by pid 1270
1265:S * AOF rewrite child asks to stop sending diffs.
1270:C * Parent agreed to stop sending diffs. Finalizing AOF...
1270:C * Concatenating 0.00 MB of AOF diff received from parent.
1270:C * SYNC append only file rewrite performed

# 2 MB数据被写入 AOF 文件
1270:C * AOF rewrite: 2 MB of memory used by copy-on-write

# 后台 AOF 进程成功终⽌
1265:S * Background AOF rewrite terminated with success
1265:S * Residual parent diff successfully flushed to the rewritten AOF (0.00 MB)

# 后台 AOF 重写顺利完成
1265:S * Background AOF rewrite finished successfully

原从节点现在是主节点

tail -f -n 1000 /usr/local/redis/cluster/log/redis-6374.log
# 清除节点 ae0b3936c837a46972055c7b9b691bdd8149b7d 的 fail 状态
1181:M * Clear FAIL state for node afe0b3936c837a46972055c7b9b691bdd8149b7d: master without slots is reachable again.
# 192.168.10.101:6371 从节点发起 SYNC 请求
1181:M * Replica 192.168.10.101:6371 asks for synchronization
# 拒绝部分重同步
1181:M * Partial resynchronization not accepted: Replication ID mismatch (Replica asked for '0e17197fbbd478907a28c62aea61f7d1486f5adf', my replication IDs are '9668c0e62ba28defda2b0360daa6b7f0fe5f89e0' and 0b35768c25c0a463cb73b9056dc832897a815ba7')
# 通过 BGSAVE 指令将数据写入磁盘(RBD操作)
1181:M * Starting BGSAVE for SYNC with target: disk
# 开启一个子守护进程执行写入
1181:M * Background saving started by pid 1216
# 数据已写入磁盘
1216:C * DB saved on disk
# 有 4MB 数据已写入磁盘
1216:C * RDB: 4 MB of memory used by copy-on-write
# 保存结束
1181:M * Background saving terminated with success
# 从节点同步数据结束
1181:M * Synchronization with replica 192.168.10.101:6371 succeeded

查看数据

原来 address 和 age 这两个 Key 都在 6371 中,现在查看一下数据是否会被转向到 6374 中去获取。

手动故障转移

​ 有的时候在主节点没有任何问题的情况下强制⼿动故障转移也是很有必要的,比如想要升级主节点的 Redis 进程,我们可以通过故障转移将其转为 Slave 再进行升级操作来避免对集群的可用性造成很大的影响。

Redis 集群使用 CLUSTER FAILOVER 命令来进行故障转移,不过要在被转移的主节点的从节点上执行该 命令。

​ 手动故障转移比主节点失败自动故障转移更加安全,因为手动故障转移时客户端的切换是在确保新的主节点完全复制了失败的旧的主节点数据的前提下下发生的,所以避免了数据的丢失。

​ 执行手动故障转移以后,客户端不再链接我们淘汰的主节点,同时主节点向从节点发送复制偏移量,从节点得到复制偏移量后故障转移开始,接着通知主节点进行配置切换。

执行命令

在刚才降级为从节点的 6371 中执行 CLUSTER FAILOVER 开启手动故障转移。

/usr/local/redis/bin/redis-cli -c -a 123456 -h 192.168.10.102 -p 6374 cluster failover

查看集群状态

/usr/local/redis/bin/redis-cli -c -a 123456 -h 192.168.10.101 -p 6372 cluster nodes

查看主从日志

原主节点现在是从节点

tail -f -n 1000 /usr/local/redis/cluster/log/redis-6374.log
# 从节点 ae0b3936c837a46972055c7b9b691bdd8149b7d 请求手动故障切换
1181:M # Manual failover requested by replica afe0b3936c837a46972055c7b9b691bdd8149b7d.

# 授予 afe0b3936c837a46972055c7b9b691bdd8149b7d epoch 8 的故障转移授权
1181:M # Failover auth granted to afe0b3936c837a46972055c7b9b691bdd8149b7d for epoch 8

# 与从节点失去联系
1181:M # Connection with replica 192.168.10.101:6371 lost.

# 检测到配置变更。将自己重新配置为 afe0b3936c837a46972055c7b9b691bdd8149b7d 的副本
1181:M # Configuration change detected. Reconfiguring myself as areplica of afe0b3936c837a46972055c7b9b691bdd8149b7d

# 变为副本前的准备工作
1181:S * Before turning into a replica, using my own master param
eters to synthesize a cached master: I may be able to synchronize
with the new master with just a partial transfer.

# SYNC 过程
1181:S * Connecting to MASTER 192.168.10.101:6371
1181:S * MASTER <-> REPLICA sync started
1181:S * Non blocking connect for SYNC fired the event.
1181:S * Master replied to PING, replication can continue...
1181:S * Trying a partial resynchronization (request 9668c0e62ba28defda2b0360daa6b7f0fe5f89e0:4537).
1181:S * Successful partial resynchronization with master.
1181:S # Master replication ID changed to aa3c95e0f57609775000dd12deaaa122feb792dc
1181:S * MASTER <-> REPLICA sync: Master accepted a Partial Resynchronization.

原从节点现在是主节点

tail -f -n 1000 /usr/local/redis/cluster/log/redis-6371.log
# 接受手动故障切换请求
1265:S # Manual failover user request accepted.
# 收到暂停服务的主节点动故障切换的复制偏移 4536
1265:S # Received replication offset for paused master manual failover: 4536
# 所有主复制流处理完毕,可以开始手动故障切换
1265:S # All master replication stream processed, manual failovercan start.
# 选举开始
1265:S # Start of election delayed for 0 milliseconds (rank #0, o
ffset 4536).
# 开始进行第8纪元的故障切换选举
1265:S # Starting a failover election for epoch 8.
# 选举成功,自己(192.168.10.101:6371)是新主节点
1265:S # Failover election won: I'm the new master.
# 故障切换成功后,配置文件 config epoch 设置为 8
1265:S # configEpoch set to 8 after successful failover
1265:M # Connection with master lost.
1265:M * Caching the disconnected master state.
# 抛弃之前缓存的主节点状态
1265:M * Discarding previously cached master state.
# 将二级主从 ID 设置为 xxx 有效偏移量为 4537 新的主从 ID 为 xxx
1265:M # Setting secondary replication ID to 9668c0e62ba28defda2b0360daa6b7f0fe5f89e0, valid up to offset: 4537. New replication ID is aa3c95e0f57609775000dd12deaaa122feb792dc
# 192.168.10.102:6374 请求同步
1265:M * Replica 192.168.10.102:6374 asks for synchronization
# 接受来自 192.168.10.102:6374 的部分重新同步请求。从偏移量 4537 开始
1265:M * Partial resynchronization request from 192.168.10.102:63 accepted. Sending 0 bytes of backlog starting from offset 4537.

查看数据

原来 address 和 age 这两个 Key 都在 6374 中,现在查看一下数据是否会被转向到 6371 中去获取。

集群迁移

手动迁移

  1. 取消密码(有密码的话,最好先不要用密码)
  2. 创建与原集群结构一致的新集群(目标集群)环境
  3. 先从后主停掉⽬标集群服务
  4. 清除目标集群所有节点 appendonly.aof 和 dump.rdb
  5. 原集群手动触发 bgrewriteaof
  6. 复制原集群所有节点 appendonly.aof 文件到目标集群
  7. 启动新集群并设置密码
  8. 检查状态,迁移完毕

Redis-Shark

​ 很多人在升级时选用唯品会开源的 redis-migrate-tool 做数据迁移,但是这个工具太老,redis4 及以上 版本的支持不太友好。

所以我们使用阿里开源的 Redis-Shark 数据迁移工具。

Redis-Shark开源项目:https://github.com/alibaba/RedisShake

Redis-Shark中文文档:https://github.com/alibaba/RedisShake/wiki/

Redis-Shark官方编译包:https://github.com/alibaba/RedisShake/releases/

环境准备

源集群:
IP端口
192.168.10.1016371、6372
192.168.10.1026373、6374
192.168.10.1036375、6376

Key 分布情况:

192.168.10.101:6371 (afe0b393...) -> 210683 keys | 5461 slots | 1 slaves.
192.168.10.102:6373 (339e0d0b...) -> 9 keys | 5462 slots | 1 slaves.
192.168.10.103:6375 (f3353d11...) -> 3 keys | 5461 slots | 1 slaves.
目标集群
IP端口
192.168.10.1046371、6372
192.168.10.1056373、6374
192.168.10.1066375、6376

Key 分布情况:

192.168.10.104:6371 (15d444c6...) -> 0 keys | 5461 slots | 1 slaves.
192.168.10.106:6375 (77772ed7...) -> 0 keys | 5461 slots | 1 slaves.
192.168.10.105:6373 (32069c23...) -> 0 keys | 5462 slots | 1 slaves.
解压工具包

下载官方编译好的工具包,直接解压后就能使用。

tar -zxvf redis-shake-v2.0.3.tar.gz
编写配置

​ 官方文档中给出了很多迁移模式的配置案例,比如单个节点到单个节点、集群版cluster到集群版cluster、 集群版cluster到proxy、主从版/单节点到cluster等等。

集群版cluster到proxy配置

redis-shark.conf

# 当前配置文件的版本号,请不要修改该值。
conf.version = 1
# 默认即可。
id = redis-shake
# 源端 Redis 的类型,支持 standalone,sentinel,cluster 和 proxy 四种模式。
source.type = cluster
# 源端所有 Master 或者 Slave 地址。
source.address = 192.168.10.101:6371;192.168.10.102:6373;192.168.10.103:6375
# 源端验证类型
source.auth_type = auth
# 源端 Redis 密码
source.password_raw = 123456
# 目标 Redis 的类型,支持 standalone,sentinel,cluster 和 proxy 四种模式。
target.type = cluster
# 目标所有 Master 或者 Slave 地址。
target.address = 192.168.10.104:6371;192.168.10.105:6373;192.168.10.106:6375
# 目标验证类型
target.auth_type = auth
# 目标 Redis 密码
target.password_raw = 123456
迁移

使用相应平台对应的 redis-shark 工具,此处是在 Linux 上做的操作,故选择 redsi-sharke.linux。

在这里插入图片描述

./redis-shake.linux -conf=redis-shake.conf -type=sync
  • -conf:指定配置文件路径
  • -type:指定操作类型
    • sync:全量+增量同步

该工具如果不停止运行,可以起到一直监听源集群的作用,当源集群有数据改动会直接备份到目标集 群。

检查

先来人工检查一下,查看两个集群的状态。

原集群:

192.168.10.101:6371 (afe0b393...) -> 210683 keys | 5461 slots | 1 slaves.
192.168.10.102:6373 (339e0d0b...) -> 9 keys | 5462 slots | 1 slaves.
192.168.10.103:6375 (f3353d11...) -> 3 keys | 5461 slots | 1 slaves.

目标集群:

192.168.10.104:6371 (15d444c6...) -> 210683 keys | 5461 slots | 1 slaves.
192.168.10.106:6375 (77772ed7...) -> 3 keys | 5461 slots | 1 slaves.
192.168.10.105:6373 (32069c23...) -> 9 keys | 5462 slots | 1 slaves.
RedisFullCheck

若要检查源和目标是否数据统一,还可选择阿里配套工具 RedisFullCheck。

RedisFullCheck开源项目:https://github.com/alibaba/RedisFullCheck

RedisFullCheck中文文档:https://github.com/alibaba/RedisFullCheck/wiki/

RedisFullCheck官方编译包:https://github.com/alibaba/RedisFullCheck/releases

下载官方编译好的工具包,直接解压后就能使用。

tar -zxvf redis-full-check-1.4.8.tar.gz

运行

./redis-full-check -s "192.168.10.101:6371;192.168.10.102:6373;192.168.10.103:6375" \
--sourcepassword=123456 -t "192.168.10.104:6371;192.168.10.105:6373;192.168.10.106:6375" --targetpassword=123456 --comparemode=1 --comparetimes=1 --qps=10 --batchcount=100 \
--sourcedbtype=1 --targetdbtype=1
  • **-s:**源redis库地址(ip:port),如果是集群版,那么需要以分号
  • **–sourcepassword:**源redis库密码 -t:目标redis库地址(ip:port)
  • **–targetpassword:**目标redis库密码
  • **–comparemode:**比较模式,1表示全量比较,2表示只对比value的长度,3只对比key是否存在,4 全量比较的情况下,忽略大key的比较
  • **–comparetimes:**比较轮数
  • **–qps:**qps限速阈值
  • **–batchcount:**批量聚合的数量
  • **–sourcedbtype:**源库的类别,0:db(standalone单节点、主从),1: cluster(集群版),2: 阿里云
  • **–targetdbtype:**目的库的类别

结果

--------------- finished! ----------------
all finish successfully, totally 0 key(s) and 0 field(s) conflict
Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐