通过集群,解决 写操作 无法 负载均衡 以及 存储能力 受到 单机限制 的问题,实现了较为 完善 的 高可用方案
Redis 集群(Redis Cluster) 是 Redis 官方提供的分布式数据库方案。
既然是分布式,自然具备分布式系统的基本特性:可扩展、高可用、一致性。
- Redis 集群通过划分 hash 槽来分区,进行数据分享。
- Redis 集群采用主从模型,提供复制和故障转移功能,来保证 Redis 集群的高可用。
- 根据 CAP 理论,Consistency、Availability、Partition tolerance 三者不可兼得,而 Redis 集群的选择是 AP。Redis 集群节点间采用异步通信方式,不保证强一致性,尽力达到最终一致性。
一、Redis Cluster 分区
1、集群节点
Redis 集群由多个节点组成,节点刚启动时,彼此是相互独立的。节点通过握手( CLUSTER MEET
命令)来将其他节点添加到自己所处的集群中。
向一个节点发送 CLUSTER MEET
命令,可以让当前节点与指定 IP、PORT 的节点进行握手,握手成功时,当前节点会将指定节点加入所在集群。
集群节点保存键值对以及过期时间的方式与单机 Redis 服务完全相同。
Redis 集群节点分为主节点(master)和从节点(slave),其中主节点用于处理槽,而从节点则用于复制某个主节点,并在被复制的主节点下线时,代替下线主节点继续处理命令请求。
2、分配 Hash 槽
分布式存储需要解决的首要问题是把 整个数据集 按照 分区规则 映射到 多个节点 的问题,即把 数据集 划分到 多个节点 上,每个节点负责 整体数据 的一个 子集。
Redis 集群通过划分 hash 槽来将数据分区。Redis 集群通过分区的方式来保存数据库的键值对:集群的整个数据库被分为 16384 个哈希槽(slot),数据库中的每个键都属于这 16384 个槽的其中一个,集群中的每个节点可以处理 0 个或最多 16384 个槽。如果数据库中有任何一个槽没有得到处理,那么集群处于下线状态。
通过向节点发送 CLUSTER ADDSLOTS
命令,可以将一个或多个槽指派给节点负责。
> CLUSTER ADDSLOTS 1 2 3
OK
集群中的每个节点负责一部分哈希槽,比如集群中有3个节点,则:
- 节点A存储的哈希槽范围是:0 – 5500
- 节点B存储的哈希槽范围是:5501 – 11000
- 节点C存储的哈希槽范围是:11001 – 16384
3、寻址
当客户端向节点发送与数据库键有关的命令时,接受命令的节点会计算出命令要处理的数据库属于哪个槽,并检查这个槽是否指派给了自己:
- 如果键所在的槽正好指派给了当前节点,那么当前节点直接执行命令。
- 如果键所在的槽没有指派给当前节点,那么节点会向客户端返回一个 MOVED 错误,指引客户端重定向至正确的节点。
1、计算键属于哪个槽
决定一个 key 应该分配到那个槽的算法是:计算该 key 的 CRC16 结果再模 16834。
HASH_SLOT = CRC16(KEY) mod 16384
当节点计算出 key 所属的槽为 i 之后,节点会根据以下条件判断槽是否由自己负责:
clusterState.slots[i] == clusterState.myself
2、MOVED 错误
当节点发现键所在的槽并非自己负责处理的时候,节点就会向客户端返回一个 MOVED
错误,指引客户端转向正在负责槽的节点。
MOVED
错误的格式为:
MOVED <slot> <ip>:<port>
个人理解:MOVED 这种操作有点类似 HTTP 协议中的重定向。
4、重新分区
Redis 集群的重新分区操作可以将任意数量的已经指派给某个节点(源节点)的槽改为指派给另一个节点(目标节点),并且相关槽所属的键值对也会从源节点被移动到目标节点。
重新分区操作可以在线进行,在重新分区的过程中,集群不需要下线,并且源节点和目标节点都可以继续处理命令请求。
Redis 集群的重新分区操作由 Redis 集群管理软件 redis-trib 负责执行的,redis-trib 通过向源节点和目标节点发送命令来进行重新分区操作。
重新分区的实现原理如下图所示:
5、ASK 错误
ASK
错误与 MOVED
的区别在于:ASK 错误只是两个节点在迁移槽的过程中使用的一种临时措施,在客户端收到关于槽 X 的 ASK 错误之后,客户端只会在接下来的一次命令请求中将关于槽 X 的命令请求发送至 ASK 错误所指示的节点,但这种转向不会对客户端今后发送关于槽 X 的命令请求产生任何影响,客户端仍然会将关于槽 X 的命令请求发送至目前负责处理槽 X 的节点,除非 ASK 错误再次出现。
判断 ASK 错误的过程如下图所示:
二、Redis Cluster 故障转移
1、复制
2、故障检测
集群中每个节点都会定期向集群中的其他节点发送 PING 消息,以此来检测对方是否在线。
节点的状态信息可以分为:
-
在线状态;
-
下线状态(FAIL);
-
疑似下线状态(PFAIL),即在规定的时间内,没有应答 PING 消息;
3、故障转移
1、下线主节点的所有从节点中,会有一个从节点被选中。
2、被选中的从节点会执行 SLAVEOF no one
命令,成为新的主节点。
3、新的主节点会撤销所有对已下线主节点的槽指派,并将这些槽全部指派给自己。
4、新的主节点向集群广播一条 PONG 消息,告知其他节点这个从节点已变成主节点。
4、选举新的主节点
Redis 集群选举新的主节点流程基于共识算法:Raft
三、Redis Cluster 通信
集群中的节点通过发送和接收消息来进行通信。
Redis 集群节点发送的消息主要有以下五种:
MEET
- 请求接收方加入发送方所在的集群。PING
- 集群中每个节点每隔一段时间(默认为一秒)从已知节点列表中随机选出五个节点,然后对这五个节点中最久没联系的节点发送 PING 消息,以此检测被选中的节点是否在线。PONG
- 当接收方收到发送方发来的 MEET 消息或 PING 消息时,会返回一条 PONG 消息作为应答。FAIL
- 当一个主节点 A 判断另一个主节点 B 已经进入 FAIL 状态时,节点 A 会向集群广播一条关于节点 B 的 FAIL 消息,所有收到这条消息的节点都会立即将节点 B 标记为已下线。PUBLISH
- 当节点收到一个 PUBLISH 命令时,节点会执行这个命令,并向集群广播一条 PUBLISH 消息,所有接受到这条消息的节点都会执行相同的 PUBLISH 命令。
四、Redis Cluster 应用
1、集群功能限制
Redis 集群相对 单机,存在一些功能限制,需要 开发人员 提前了解,在使用时做好规避。
-
key
批量操作 支持有限:类似mset
、mget
操作,目前只支持对具有相同slot
值的key
执行 批量操作。对于 映射为不同slot
值的key
由于执行mget
、mget
等操作可能存在于多个节点上,因此不被支持。 -
key
事务操作 支持有限:只支持 多key
在 同一节点上 的 事务操作,当多个key
分布在 不同 的节点上时 无法 使用事务功能。 -
key
作为 数据分区 的最小粒度,不能将一个 大的键值 对象如hash
、list
等映射到 不同的节点。 -
不支持 多数据库空间:单机 下的 Redis 可以支持
16
个数据库(db0 ~ db15
),集群模式 下只能使用 一个 数据库空间,即db0
。 -
复制结构 只支持一层:从节点 只能复制 主节点,不支持 嵌套树状复制 结构。
2、集群规模限制
Redis Cluster 的优点是易于使用。分区、主从复制、弹性扩容这些功能都可以做到自动化,通过简单的部署就可以获得一个大容量、高可靠、高可用的 Redis 集群,并且对于应用来说,近乎于是透明的。
所以,Redis Cluster 非常适合构建中小规模 Redis 集群,这里的中小规模指的是,大概几个到几十个节点这样规模的 Redis 集群。
但是 Redis Cluster 不太适合构建超大规模集群,主要原因是,它采用了去中心化的设计。
Redis 的每个节点上,都保存了所有槽和节点的映射关系表,客户端可以访问任意一个节点,再通过重定向命令,找到数据所在的那个节点。那么,这个映射关系表是如何更新的呢?Redis Cluster 采用了一种去中心化的流言 (Gossip) 协议来传播集群配置的变化。
Gossip 协议的优点是去中心化;缺点是传播速度慢,并且是集群规模越大,传播的越慢。
3、集群配置
我们后面会部署一个 Redis 集群作为例子,在那之前,先介绍一下集群在 redis.conf 中的参数。
- cluster-enabled
<yes/no>
- 如果配置”yes”则开启集群功能,此 redis 实例作为集群的一个节点,否则,它是一个普通的单一的 redis 实例。 - cluster-config-file
<filename>
- 注意:虽然此配置的名字叫“集群配置文件”,但是此配置文件不能人工编辑,它是集群节点自动维护的文件,主要用于记录集群中有哪些节点、他们的状态以及一些持久化参数等,方便在重启时恢复这些状态。通常是在收到请求之后这个文件就会被更新。 - cluster-node-timeout
<milliseconds>
- 这是集群中的节点能够失联的最大时间,超过这个时间,该节点就会被认为故障。如果主节点超过这个时间还是不可达,则用它的从节点将启动故障迁移,升级成主节点。注意,任何一个节点在这个时间之内如果还是没有连上大部分的主节点,则此节点将停止接收任何请求。 - cluster-slave-validity-factor
<factor>
- 如果设置成0,则无论从节点与主节点失联多久,从节点都会尝试升级成主节点。如果设置成正数,则 cluster-node-timeout 乘以 cluster-slave-validity-factor 得到的时间,是从节点与主节点失联后,此从节点数据有效的最长时间,超过这个时间,从节点不会启动故障迁移。假设 cluster-node-timeout=5,cluster-slave-validity-factor=10,则如果从节点跟主节点失联超过 50 秒,此从节点不能成为主节点。注意,如果此参数配置为非 0,将可能出现由于某主节点失联却没有从节点能顶上的情况,从而导致集群不能正常工作,在这种情况下,只有等到原来的主节点重新回归到集群,集群才恢复运作。 - cluster-migration-barrier
<count>
- 主节点需要的最小从节点数,只有达到这个数,主节点失败时,它从节点才会进行迁移。更详细介绍可以看本教程后面关于副本迁移到部分。 - cluster-require-full-coverage
<yes/no>
- 在部分 key 所在的节点不可用时,如果此参数设置为”yes”(默认值), 则整个集群停止接受操作;如果此参数设置为”no”,则集群依然为可达节点上的 key 提供读操作。
五、其他 Redis 集群方案
Redis Cluster 不太适合用于大规模集群,所以,如果要构建超大 Redis 集群,需要选择替代方案。一般有三种方案类型:
- 客户端分区方案
- 代理分区方案
- 查询路由方案
1、客户端分区方案
客户端 就已经决定数据会被 存储 到哪个 Redis 节点或者从哪个 Redis 节点 读取数据。其主要思想是采用 哈希算法 将 Redis 数据的 key
进行散列,通过 hash
函数,特定的 key
会 映射 到特定的 Redis 节点上。
客户端分区方案 的代表为 Redis Sharding,Redis Sharding 是 Redis Cluster 出来之前,业界普遍使用的 Redis 多实例集群 方法。Java 的 Redis 客户端驱动库 Jedis,支持 Redis Sharding 功能,即 ShardedJedis 以及 结合缓存池 的 ShardedJedisPool。
-
优点:不使用 第三方中间件,分区逻辑 可控,配置 简单,节点之间无关联,容易 线性扩展,灵活性强。
-
缺点:客户端 无法 动态增删 服务节点,客户端需要自行维护 分发逻辑,客户端之间 无连接共享,会造成 连接浪费。
2、代理分区方案
客户端 发送请求到一个 代理组件,代理 解析 客户端 的数据,并将请求转发至正确的节点,最后将结果回复给客户端。
- 优点:简化 客户端 的分布式逻辑,客户端 透明接入,切换成本低,代理的 转发 和 存储 分离。
- 缺点:多了一层 代理层,加重了 架构部署复杂度 和 性能损耗。
代理分区 主流实现的有方案有 Twemproxy 和 Codis。
1、Twemproxy
Twemproxy 也叫 nutcraker
,是 Twitter 开源的一个 Redis 和 Memcache 的 中间代理服务器 程序。
Twemproxy 作为 代理,可接受来自多个程序的访问,按照 路由规则,转发给后台的各个 Redis 服务器,再原路返回。Twemproxy 存在 单点故障 问题,需要结合 Lvs 和 Keepalived 做 高可用方案。
- 优点:应用范围广,稳定性较高,中间代理层 高可用。
- 缺点:无法平滑地 水平扩容/缩容,无 可视化管理界面,运维不友好,出现故障,不能 自动转移。
2、Codis
Codis 是一个 分布式 Redis 解决方案,对于上层应用来说,连接 Codis-Proxy 和直接连接 原生的 Redis-Server 没有的区别。Codis 底层会 处理请求的转发,不停机的进行 数据迁移 等工作。Codis 采用了无状态的 代理层,对于 客户端 来说,一切都是透明的。
-
优点:实现了上层 Proxy 和底层 Redis 的 高可用,数据分区 和 自动平衡,提供 命令行接口 和 RESTful API,提供 监控 和 管理 界面,可以动态 添加 和 删除 Redis 节点。
-
缺点:部署架构 和 配置 复杂,不支持 跨机房 和 多租户,不支持 鉴权管理。
3、查询路由方案
客户端随机地 请求任意一个 Redis 实例,然后由 Redis 将请求 转发 给 正确 的 Redis 节点。Redis Cluster 实现了一种 混合形式 的 查询路由,但并不是 直接 将请求从一个 Redis 节点 转发 到另一个 Redis 节点,而是在 客户端 的帮助下直接 重定向( redirected
)到正确的 Redis 节点。
-
优点:去中心化,数据按照 槽 存储分布在多个 Redis 实例上,可以平滑的进行节点 扩容/缩容,支持 高可用 和 自动故障转移,运维成本低。
-
缺点:重度依赖 Redis-trib 工具,缺乏 监控管理,需要依赖 Smart Client (维护连接,缓存路由表,
MultiOp
和Pipeline
支持)。Failover 节点的 检测过慢,不如有 中心节点 的集群及时(如 ZooKeeper)。Gossip 消息采用广播方式,集群规模越大,开销越大。无法根据统计区分 冷热数据。
六、手动搭建部署集群
::: warning
按预期工作的最小集群必须至少包含三个主节点。对于部署,我们强烈建议使用具有三个主节点和三个副本的六节点集群。
:::
1、安装redis集群节点
redis1操作
# mkdir -p /opt/redis_cluster/redis_{6380,6381}/{conf,logs,pid} //redis安装目录
# mkdir -p /data/redis_cluster/redis_{6380,6381} //redis数据目录
# vim /opt/redis_cluster/redis_6380/conf/redis_6380.conf
bind 192.168.1.10
port 6380
daemonize yes
pidfile "/opt/redis_cluster/redis_6380/pid/redis_6380.pid"
logfile "/opt/redis_cluster/redis_6380/logs/redis_6380.log"
dbfilename "redis_6380.rdb"
dir "/data/redis_cluster/redis_6380/"
appendonly yes
# 如果是,则在特定的 Redis 实例中启用 Redis 集群支持。否则实例将像往常一样作为独立实例启动
cluster-enabled yes
# 请注意,尽管有此选项的名称,但这不是用户可编辑的配置文件,而是 Redis 集群节点在每次发生更改时自动保存集群配置(基本上是状态)的文件
cluster-config-file nodes_6380.conf
# Redis 集群节点可以不可用的最长时间,不会被视为失败
cluster-node-timeout 15000
# cd /opt/redis_cluster/
# cp redis_6380/conf/redis_6380.conf redis_6381/conf/redis_6381.conf
# sed -i 's#6380#6381#g' redis_6381/conf/redis_6381.conf
# redis-server /opt/redis_cluster/redis_6380/conf/redis_6380.conf
# redis-server /opt/redis_cluster/redis_6381/conf/redis_6381.conf
# cd
# scp -rp /opt/redis_cluster/ root@192.168.1.20:/opt/ //复制redis1的安装和数据目录到redis2、redis3
# scp -rp /opt/redis_cluster/ root@192.168.1.30:/opt/
redis2操作
# cd /opt/redis_cluster/redis
# make install
# find /opt/redis_cluster/redis_638* -type f -name "*.conf" | xargs sed -i "s#10#20#g"
# mkdir -p /data/redis_cluster/redis_{6380,6381}
# redis-server /opt/redis_cluster/redis_6380/conf/redis_6380.conf
# redis-server /opt/redis_cluster/redis_6381/conf/redis_6381.conf
redis3操作
# cd /opt/redis_cluster/redis
# make install
# find /opt/redis_cluster/redis_638* -type f -name "*.conf"|xargs sed -i "s#10#30#g"
# mkdir -p /data/redis_cluster/redis_{6380,6381}
# redis-server /opt/redis_cluster/redis_6380/conf/redis_6380.conf
# redis-server /opt/redis_cluster/redis_6381/conf/redis_6381.conf
# ps aux | grep redis
root 35574 0.0 0.4 153836 7712 ? Ssl 15:45 0:00 redis-server 192.168.1.30:6380 [cluster]
root 35663 0.0 0.4 153836 7716 ? Ssl 15:45 0:00 redis-server 192.168.1.30:6381 [cluster]
root 36258 0.0 0.0 112664 1008 pts/0 S+ 15:47 0:00 grep --color=auto redis
2、手动配置节点发现
# vim redis_shell.sh //redis运维脚本
#!/bin/bash
USAG(){
echo "sh $0 {start|stop|restart|login|ps|tail} PORT"
}
if [ "$#" = 1 ]
then
REDIS_PORT='6379'
elif
[ "$#" = 2 -a -z "$(echo "$2"|sed 's#[0-9]##g')" ]
then
REDIS_PORT="$2"
else
USAG
exit 0
fi
REDIS_IP=$(hostname -I|awk '{print $1}')
PATH_DIR=/opt/redis_cluster/redis_${REDIS_PORT}/
PATH_CONF=/opt/redis_cluster/redis_${REDIS_PORT}/conf/redis_${REDIS_PORT}.conf
PATH_LOG=/opt/redis_cluster/redis_${REDIS_PORT}/logs/redis_${REDIS_PORT}.log
CMD_START(){
redis-server ${PATH_CONF}
}
CMD_SHUTDOWN(){
redis-cli -c -h ${REDIS_IP} -p ${REDIS_PORT} shutdown
}
CMD_LOGIN(){
redis-cli -c -h ${REDIS_IP} -p ${REDIS_PORT}
}
CMD_PS(){
ps -ef|grep redis
}
CMD_TAIL(){
tail -f ${PATH_LOG}
}
case $1 in
start)
CMD_START
CMD_PS
;;
stop)
CMD_SHUTDOWN
CMD_PS
;;
restart)
CMD_START
CMD_SHUTDOWN
CMD_PS
;;
login)
CMD_LOGIN
;;
ps)
CMD_PS
;;
tail)
CMD_TAIL
;;
*)
USAG
esac
# sh redis_shell.sh login 6380
192.168.1.10:6380> CLUSTER MEET 192.168.1.10 6381 //将192.168.1.10:6381添加到集群当中
OK
192.168.1.10:6380> CLUSTER MEET 192.168.1.20 6380
OK
192.168.1.10:6380> CLUSTER MEET 192.168.1.30 6380
OK
192.168.1.10:6380> CLUSTER MEET 192.168.1.20 6381
OK
192.168.1.10:6380> CLUSTER MEET 192.168.1.30 6381
OK
192.168.1.10:6380> CLUSTER NODES //列出集群当前已知的所有节点
8c57f9393090404c0ada0d258a964dec3583f191 192.168.1.10:6381@16381 master - 0 1596032980568 1 connected
f0672e893f80b751abe49862aafb7740dfa34506 192.168.1.30:6381@16381 master - 0 1596032981000 5 connected
c92419d7d715447a422df4e954d31d924a85b39c 192.168.1.20:6380@16380 master - 0 1596032978000 4 connected
f57ff74970ef3c24d450e1b141ee911e91bc6350 192.168.1.20:6381@16381 master - 0 1596032981585 0 connected
b25723025c04492fac759645d75477efc01d7dbd 192.168.1.10:6380@16380 myself,master - 0 1596032979000 2 connected
0a2778d9265a83bf4708fc496c7ea6d9a3f6b9e5 192.168.1.30:6380@16380 master - 0 1596032979544 3 connected
Redis集群常用命令 | 说明 |
---|---|
CLUSTER INFO | 打印集群的信息 |
CLUSTER NODES | 列出集群当前已知的所有节点(node),以及这些节点的相关信息 |
CLUSTER MEET ip port | 将 ip 和 port 所指定的节点添加到集群当中,让它成为集群的一份子 |
CLUSTER FORGET node_id | 从集群中移除 node_id 指定的节点 |
CLUSTER REPLICATE node_id | 将当前节点设置为 node_id 指定的节点的从节点 |
CLUSTER SAVECONFIG | 将节点的配置文件保存到硬盘里面 |
CLUSTER ADDSLOTS slot [slot ...] | 将一个或多个槽(slot)指派(assign)给当前节点 |
CLUSTER DELSLOTS slot [slot ...] | 移除一个或多个槽对当前节点的指派 |
CLUSTER FLUSHSLOTS | 移除指派给当前节点的所有槽,让当前节点变成一个没有指派任何槽的节点 |
CLUSTER SETSLOT slot NODE node_id | 将槽 slot 指派给 node_id 指定的节点,如果槽已经指派给另一个节点,那么先让另一个节点删除该槽,然后再进行指派 |
CLUSTER SETSLOT slot MIGRATING node_id | 将本节点的槽 slot 迁移到 node_id 指定的节点中 |
CLUSTER SETSLOT slot IMPORTING node_id | 从 node_id 指定的节点中导入槽 slot 到本节点 |
CLUSTER SETSLOT slot STABLE | 取消对槽 slot 的导入(import)或者迁移(migrate) |
CLUSTER KEYSLOT key | 计算键 key 应该被放置在哪个槽上 |
CLUSTER COUNTKEYSINSLOT slot | 返回槽 slot 目前包含的键值对数量 |
CLUSTER GETKEYSINSLOT slot count | 返回 count 个 slot 槽中的键 |
3、Redis Cluster手动分配槽位
# redis-cli -h 192.168.1.10 -p 6380 cluster addslots {0..5461} //分配槽位
OK
# redis-cli -h 192.168.1.20 -p 6380 cluster addslots {5462..10922}
OK
# redis-cli -h 192.168.1.30 -p 6380 cluster addslots {10923..16383}
OK
# sh redis_shell.sh login 6380
192.168.1.10:6380> CLUSTER NODES
8c57f9393090404c0ada0d258a964dec3583f191 192.168.1.10:6381@16381 slave c92419d7d715447a422df4e954d31d924a85b39c 0 1596033441077 4 connected
f0672e893f80b751abe49862aafb7740dfa34506 192.168.1.30:6381@16381 slave b25723025c04492fac759645d75477efc01d7dbd 0 1596033438029 5 connected
c92419d7d715447a422df4e954d31d924a85b39c 192.168.1.20:6380@16380 master - 0 1596033442088 4 connected 5462-10922 //已分配(主节点)
f57ff74970ef3c24d450e1b141ee911e91bc6350 192.168.1.20:6381@16381 slave 0a2778d9265a83bf4708fc496c7ea6d9a3f6b9e5 0 1596033439041 3 connected
b25723025c04492fac759645d75477efc01d7dbd 192.168.1.10:6380@16380 myself,master - 0 1596033441000 2 connected 0-5461 //已分配(主节点)
0a2778d9265a83bf4708fc496c7ea6d9a3f6b9e5 192.168.1.30:6380@16380 master - 0 1596033440062 3 connected 10923-16383 //已分配(主节点)
# redis-cli -h 192.168.1.10 -p 6381 CLUSTER REPLICATE c92419d7d715447a422df4e954d31d924a85b39c //将当前节点设置为192.168.1.20:6380的从节点
OK
# redis-cli -h 192.168.1.20 -p 6381 CLUSTER REPLICATE 0a2778d9265a83bf4708fc496c7ea6d9a3f6b9e5 //将当前节点设置为192.168.1.30:6380的从节点
OK
# redis-cli -h 192.168.1.30 -p 6381 CLUSTER REPLICATE b25723025c04492fac759645d75477efc01d7dbd //将当前节点设置为192.168.1.10:6380的从节点
OK
# sh redis_shell.sh login 6380
192.168.1.10:6380> 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:2
cluster_stats_messages_ping_sent:482
cluster_stats_messages_pong_sent:505
cluster_stats_messages_meet_sent:5
cluster_stats_messages_sent:992
cluster_stats_messages_ping_received:505
cluster_stats_messages_pong_received:487
cluster_stats_messages_received:992
192.168.1.10:6380> CLUSTER NODES
8c57f9393090404c0ada0d258a964dec3583f191 192.168.1.10:6381@16381 slave c92419d7d715447a422df4e954d31d924a85b39c 0 1596033441077 4 connected
f0672e893f80b751abe49862aafb7740dfa34506 192.168.1.30:6381@16381 slave b25723025c04492fac759645d75477efc01d7dbd 0 1596033438029 5 connected
c92419d7d715447a422df4e954d31d924a85b39c 192.168.1.20:6380@16380 master - 0 1596033442088 4 connected 5462-10922
f57ff74970ef3c24d450e1b141ee911e91bc6350 192.168.1.20:6381@16381 slave 0a2778d9265a83bf4708fc496c7ea6d9a3f6b9e5 0 1596033439041 3 connected
b25723025c04492fac759645d75477efc01d7dbd 192.168.1.10:6380@16380 myself,master - 0 1596033441000 2 connected 0-5461
0a2778d9265a83bf4708fc496c7ea6d9a3f6b9e5 192.168.1.30:6380@16380 master - 0 1596033440062 3 connected 10923-16383
4、Redis Cluster ASK路由测试
# vim input_key.sh
#!/bin/bash
for i in $(seq 1 1000)
do
redis-cli -c -h 192.168.1.10 -p 6380 set k_${i} v_${i} && echo "set k_${i} is ok"
done
# sh input_key.sh
# redis-cli -c -h 192.168.1.10 -p 6380
192.168.1.10:6380> get k_1
"v_1"
192.168.1.10:6380> get k_100
-> Redirected to slot [5541] located at 192.168.1.20:6380 //定位到192.168.1.20:6380
"v_100"
192.168.1.20:6380> get k_1000
-> Redirected to slot [79] located at 192.168.1.10:6380 //定位到192.168.1.10:6380
"v_1000"
192.168.1.10:6380> exit
5、模拟故障转移
1、终止服务
# pkill redis-server
2、分析日志
# sh redis_shell.sh tail 6380
73050:M 29 Jul 2020 22:29:10.145 # IP address for this node updated to 192.168.1.10
73050:M 29 Jul 2020 22:36:08.157 # Cluster state changed: ok
73050:M 29 Jul 2020 22:37:03.562 * Replica 192.168.1.30:6381 asks for synchronization
73050:M 29 Jul 2020 22:37:03.562 * Partial resynchronization not accepted: Replication ID mismatch (Replica asked for '448427f9eaf3c5872f0ee92431a3ff457ee09f4a', my replication IDs are '457f10755e59ab129adfdab9af3af316bb54ece1' and '0000000000000000000000000000000000000000')
73050:M 29 Jul 2020 22:37:03.562 * Starting BGSAVE for SYNC with target: disk
73050:M 29 Jul 2020 22:37:03.565 * Background saving started by pid 73274
73274:C 29 Jul 2020 22:37:03.569 * DB saved on disk
73274:C 29 Jul 2020 22:37:03.569 * RDB: 4 MB of memory used by copy-on-write
73050:M 29 Jul 2020 22:37:03.657 * Background saving terminated with success
73050:M 29 Jul 2020 22:37:03.657 * Synchronization with replica 192.168.1.30:6381 succeeded
73050:M 29 Jul 2020 22:52:03.297 * Marking node c92419d7d715447a422df4e954d31d924a85b39c as failing (quorum reached). //192.168.1.20:6380连接失败
73050:M 29 Jul 2020 22:52:03.297 * Marking node f57ff74970ef3c24d450e1b141ee911e91bc6350 as failing (quorum reached). //192.168.1.20:6381连接失败
73050:M 29 Jul 2020 22:52:03.298 # Cluster state changed: fail //集群状态更改:失败
73050:M 29 Jul 2020 22:52:04.060 # Failover auth granted to 8c57f9393090404c0ada0d258a964dec3583f191 for epoch 6 //故障转移授权
73050:M 29 Jul 2020 22:52:04.101 # Cluster state changed: ok //集群状态更改
3、重新添加集群
# redis-server /opt/redis_cluster/redis_6380/conf/redis_6380.conf
# redis-server /opt/redis_cluster/redis_6381/conf/redis_6381.conf
# sh redis_shell.sh login 6380 //将192.168.1.10:6380添加到集群当中
192.168.1.10:6380> CLUSTER NODES
8c57f9393090404c0ada0d258a964dec3583f191 192.168.1.10:6381@16381 master - 0 1596034622000 6 connected 5462-10922
f0672e893f80b751abe49862aafb7740dfa34506 192.168.1.30:6381@16381 slave b25723025c04492fac759645d75477efc01d7dbd 0 1596034624939 5 connected
c92419d7d715447a422df4e954d31d924a85b39c 192.168.1.20:6380@16380 slave 8c57f9393090404c0ada0d258a964dec3583f191 0 1596034624000 6 connected
f57ff74970ef3c24d450e1b141ee911e91bc6350 192.168.1.20:6381@16381 slave 0a2778d9265a83bf4708fc496c7ea6d9a3f6b9e5 0 1596034623925 3 connected
b25723025c04492fac759645d75477efc01d7dbd 192.168.1.10:6380@16380 myself,master - 0 1596034622000 2 connected 0-5461
0a2778d9265a83bf4708fc496c7ea6d9a3f6b9e5 192.168.1.30:6380@16380 master - 0 1596034622918 3 connected 10923-16383
192.168.1.10:6380> exit
# redis-server /opt/redis_cluster/redis_6380/conf/redis_6380.conf //重新启动redis2的服务
# redis-server /opt/redis_cluster/redis_6381/conf/redis_6381.conf
# redis-cli -h 192.168.1.20 -p 6380 CLUSTER FAILOVER
OK //redis2重回master
# sh redis_shell.sh login 6380
192.168.1.10:6380> CLUSTER NODES
8c57f9393090404c0ada0d258a964dec3583f191 192.168.1.10:6381@16381 slave c92419d7d715447a422df4e954d31d924a85b39c 0 1596034670671 7 connected
f0672e893f80b751abe49862aafb7740dfa34506 192.168.1.30:6381@16381 slave b25723025c04492fac759645d75477efc01d7dbd 0 1596034667605 5 connected
c92419d7d715447a422df4e954d31d924a85b39c 192.168.1.20:6380@16380 master - 0 1596034669651 7 connected 5462-10922 //已恢复
f57ff74970ef3c24d450e1b141ee911e91bc6350 192.168.1.20:6381@16381 slave 0a2778d9265a83bf4708fc496c7ea6d9a3f6b9e5 0 1596034670000 3 connected
b25723025c04492fac759645d75477efc01d7dbd 192.168.1.10:6380@16380 myself,master - 0 1596034669000 2 connected 0-5461
0a2778d9265a83bf4708fc496c7ea6d9a3f6b9e5 192.168.1.30:6380@16380 master - 0 1596034671691 3 connected 10923-16383
192.168.1.10:6380> exit
七、使用工具自动搭建部署Redis Cluster
1、创建集群
这里使用的命令是create,因为我们要创建一个新集群。该选项--cluster-replicas 1
意味着我们希望为每个创建的母版创建一个副本,-a 'SBKpMkYm9k9ZLPhY'-a '123456789'
表示redis所设置的密码,即
# redis-cli --cluster create --cluster-replicas 1 192.168.1.10:6380 192.168.1.10:6381 192.168.1.20:6380 192.168.1.20:6381 192.168.1.30:6380 192.168.1.30:6381 -a '123456789'
>>> Performing hash slots allocation on 6 nodes...
Master[0] -> Slots 0 - 5460
Master[1] -> Slots 5461 - 10922
Master[2] -> Slots 10923 - 16383
Adding replica 192.168.1.20:6381 to 192.168.1.10:6380
Adding replica 192.168.1.30:6381 to 192.168.1.20:6380
Adding replica 192.168.1.10:6381 to 192.168.1.30:6380
M: 250c5fe40651229a69c8ba3ca15ca0fab390a645 192.168.1.10:6380
slots:[0-5460] (5461 slots) master
S: 5b350813df4fd301d909872796840996eff4faa4 192.168.1.10:6381
replicates 16afd3566f680254c395af0079d8c0e9f55140ea
M: d46ec19a9d4d5cae7bf1585d47ccb2690f60658d 192.168.1.20:6380
slots:[5461-10922] (5462 slots) master
S: 4c4bb55ced287945d0394de8cbccd10839f51a48 192.168.1.20:6381
replicates 250c5fe40651229a69c8ba3ca15ca0fab390a645
M: 16afd3566f680254c395af0079d8c0e9f55140ea 192.168.1.30:6380
slots:[10923-16383] (5461 slots) master
S: 23a24197ff1f3472629dbb967fea642bd6fb7975 192.168.1.30:6381
replicates d46ec19a9d4d5cae7bf1585d47ccb2690f60658d
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 192.168.1.10:6380)
M: 250c5fe40651229a69c8ba3ca15ca0fab390a645 192.168.1.10:6380
slots:[0-5460] (5461 slots) master
1 additional replica(s)
M: 16afd3566f680254c395af0079d8c0e9f55140ea 192.168.1.30:6380
slots:[10923-16383] (5461 slots) master
1 additional replica(s)
M: d46ec19a9d4d5cae7bf1585d47ccb2690f60658d 192.168.1.20:6380
slots:[5461-10922] (5462 slots) master
1 additional replica(s)
S: 23a24197ff1f3472629dbb967fea642bd6fb7975 192.168.1.30:6381
slots: (0 slots) slave
replicates d46ec19a9d4d5cae7bf1585d47ccb2690f60658d
S: 5b350813df4fd301d909872796840996eff4faa4 192.168.1.10:6381
slots: (0 slots) slave
replicates 16afd3566f680254c395af0079d8c0e9f55140ea
S: 4c4bb55ced287945d0394de8cbccd10839f51a48 192.168.1.20:6381
slots: (0 slots) slave
replicates 250c5fe40651229a69c8ba3ca15ca0fab390a645
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
# redis-cli --cluster check 192.168.1.10:6380
192.168.1.10:6380 (250c5fe4...) -> 0 keys | 5461 slots | 1 slaves.
192.168.1.30:6380 (16afd356...) -> 0 keys | 5461 slots | 1 slaves.
192.168.1.20:6380 (d46ec19a...) -> 0 keys | 5462 slots | 1 slaves.
[OK] 0 keys in 3 masters.
0.00 keys per slot on average.
>>> Performing Cluster Check (using node 192.168.1.10:6380)
M: 250c5fe40651229a69c8ba3ca15ca0fab390a645 192.168.1.10:6380
slots:[0-5460] (5461 slots) master
1 additional replica(s)
M: 16afd3566f680254c395af0079d8c0e9f55140ea 192.168.1.30:6380
slots:[10923-16383] (5461 slots) master
1 additional replica(s)
M: d46ec19a9d4d5cae7bf1585d47ccb2690f60658d 192.168.1.20:6380
slots:[5461-10922] (5462 slots) master
1 additional replica(s)
S: 23a24197ff1f3472629dbb967fea642bd6fb7975 192.168.1.30:6381
slots: (0 slots) slave
replicates d46ec19a9d4d5cae7bf1585d47ccb2690f60658d
S: 5b350813df4fd301d909872796840996eff4faa4 192.168.1.10:6381
slots: (0 slots) slave
replicates 16afd3566f680254c395af0079d8c0e9f55140ea
S: 4c4bb55ced287945d0394de8cbccd10839f51a48 192.168.1.20:6381
slots: (0 slots) slave
replicates 250c5fe40651229a69c8ba3ca15ca0fab390a645
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
2、工具扩容节点
# mkdir -p /opt/redis_cluster/redis_{6390,6391}/{conf,logs,pid} //创建2个新节点
# mkdir -p /data/redis_cluster/redis_{6390,6391}
# cd /opt/redis_cluster/
# cp redis_6380/conf/redis_6380.conf redis_6390/conf/redis_6390.conf
# cp redis_6380/conf/redis_6380.conf redis_6391/conf/redis_6391.conf
# sed -i 's#6380#6390#g' redis_6390/conf/redis_6390.conf
# sed -i 's#6380#6391#g' redis_6391/conf/redis_6391.conf
# cd
# bash redis_shell.sh start 6390 //启动节点
root 74661 1 0 7月29 ? 00:00:00 redis-server 192.168.1.10:6380 [cluster]
root 74672 1 0 7月29 ? 00:00:00 redis-server 192.168.1.10:6381 [cluster]
root 75289 11092 0 00:03 pts/0 00:00:00 bash redis_shell.sh start 6390
root 75297 1 0 00:03 ? 00:00:00 redis-server 192.168.1.10:6390 [cluster]
root 75299 75289 0 00:03 pts/0 00:00:00 grep redis
# bash redis_shell.sh start 6391
root 74661 1 0 7月29 ? 00:00:00 redis-server 192.168.1.10:6380 [cluster]
root 74672 1 0 7月29 ? 00:00:00 redis-server 192.168.1.10:6381 [cluster]
root 75297 1 0 00:03 ? 00:00:00 redis-server 192.168.1.10:6390 [cluster]
root 75309 11092 0 00:03 pts/0 00:00:00 bash redis_shell.sh start 6391
root 75317 1 0 00:03 ? 00:00:00 redis-server 192.168.1.10:6391 [cluster]
root 75319 75309 0 00:03 pts/0 00:00:00 grep redis
# redis-cli --cluster add-node 192.168.1.10:6390 192.168.1.10:6380 //发现节点
>>> Adding node 192.168.1.10:6390 to cluster 192.168.1.10:6380
>>> Performing Cluster Check (using node 192.168.1.10:6380)
M: 250c5fe40651229a69c8ba3ca15ca0fab390a645 192.168.1.10:6380
slots:[0-5460] (5461 slots) master
1 additional replica(s)
M: 16afd3566f680254c395af0079d8c0e9f55140ea 192.168.1.30:6380
slots:[10923-16383] (5461 slots) master
1 additional replica(s)
M: d46ec19a9d4d5cae7bf1585d47ccb2690f60658d 192.168.1.20:6380
slots:[5461-10922] (5462 slots) master
1 additional replica(s)
S: 5b350813df4fd301d909872796840996eff4faa4 192.168.1.10:6381
slots: (0 slots) slave
replicates 16afd3566f680254c395af0079d8c0e9f55140ea
S: 23a24197ff1f3472629dbb967fea642bd6fb7975 192.168.1.30:6381
slots: (0 slots) slave
replicates d46ec19a9d4d5cae7bf1585d47ccb2690f60658d
S: 4c4bb55ced287945d0394de8cbccd10839f51a48 192.168.1.20:6381
slots: (0 slots) slave
replicates 250c5fe40651229a69c8ba3ca15ca0fab390a645
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
>>> Send CLUSTER MEET to node 192.168.1.10:6390 to make it join the cluster.
[OK] New node added correctly.
# redis-cli --cluster add-node 192.168.1.10:6391 192.168.1.10:6380
>>> Adding node 192.168.1.10:6391 to cluster 192.168.1.10:6380
>>> Performing Cluster Check (using node 192.168.1.10:6380)
M: 250c5fe40651229a69c8ba3ca15ca0fab390a645 192.168.1.10:6380
slots:[0-5460] (5461 slots) master
1 additional replica(s)
M: 16afd3566f680254c395af0079d8c0e9f55140ea 192.168.1.30:6380
slots:[10923-16383] (5461 slots) master
1 additional replica(s)
M: 10c89190ebac6dfb98d84b9335c6a573bc11663e 192.168.1.10:6390
slots: (0 slots) master
M: d46ec19a9d4d5cae7bf1585d47ccb2690f60658d 192.168.1.20:6380
slots:[5461-10922] (5462 slots) master
1 additional replica(s)
S: 5b350813df4fd301d909872796840996eff4faa4 192.168.1.10:6381
slots: (0 slots) slave
replicates 16afd3566f680254c395af0079d8c0e9f55140ea
S: 23a24197ff1f3472629dbb967fea642bd6fb7975 192.168.1.30:6381
slots: (0 slots) slave
replicates d46ec19a9d4d5cae7bf1585d47ccb2690f60658d
S: 4c4bb55ced287945d0394de8cbccd10839f51a48 192.168.1.20:6381
slots: (0 slots) slave
replicates 250c5fe40651229a69c8ba3ca15ca0fab390a645
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
>>> Send CLUSTER MEET to node 192.168.1.10:6391 to make it join the cluster.
[OK] New node added correctly.
# redis-cli --cluster rebalance 192.168.1.10:6380
>>> Performing Cluster Check (using node 192.168.1.10:6380)
[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、工具收缩节点
# redis-cli --cluster reshard 192.168.1.10:6380
4、忘记节点
# redis-cli --cluster del-node 192.168.1.10:6391 ID
# redis-cli --cluster del-node 192.168.1.10:6390 ID
评论区