redis集群的三种方式(Redis集群原理与容器化部署)
集群简单的说就是将同一个服务部署在不同的机器上,从而提高服务的横向扩展能力。
分区就是将数据分布在多个实例(服务器)上,让每一个实例都只存储一部分数据,从而达到增大总的存储数据量的效果。
1.2、为什么要实用集群?为什么要实用集群呢?主从 哨兵模式不是已经很好了吗,已经高可用了吗?
但是主从 哨兵虽然解决了高可用问题,但是没有解决数据分区存储问题。因为我们存储的数据量大小取决于主服务器的存储容量。那么集群模式将数据分区存储就是为了实现数据存储量可以横向扩展。
1.3、数据分区的优点与缺点?优点
- 1、性能提升
- 单机Redis的网络I/O能力和计算能力是有限的,将请求分发到多台机器,充分利用多台机器的计算能力和带宽,有助于redis总体的服务能力。
- 2、存储能力横向扩展
- 即使Redis的存储能力已经能满足大部分需求,但是随着key的数量不断增加,单台机器受限于机器本身的存储容量,将数据分散到多台机器存储使得Redis服务可以横向扩展。
缺点
- 1、管理更加复杂
- Redis数据分散到多台机器,管理起来自然会更加的复杂。还涉及到数据迁移等工作,增大了操作难度。
- 2、分散后的数据节点宕机使得集群缺少部分数据(可以使用主从解决)
一般按照分区键(ID)进行分区,分区方式一般有范围分区和Hash分区方式。
1.4.1、范围分区根据ID数字额范围分区,比如:1-10000,100001-20000...90001-100000。每个范围分到不同得Redis实例中。
优点
实现简单,方便迁移和扩展
缺点
数据分布不均匀,可以少数节点占据了大多数的数据。性能损失比较严重。
非数字型的key,比如UUID就无法使用,当然,可以使用雪花算法代替ID的生成。雪花算法是数值且能够排序。
1.4.2、Hash分区利用简单的hash算法就可以,比如根据key求hash值,对Redis实例的总数执行取模操作,从而计算落在哪个Redis实例上。
优点
支持所有key类型, key分布均匀, 性能比较好
缺点
迁移复杂,需要重新计算,扩展较差(但是可以利用一致性hash环拓展)。 而Hash算法在客户端链接服务端时,被广泛使用(比如:JedisPool)。
1.5、客户端分区对于给定一个key, 客户端可以直接选择正确的节点进行读写。许多客户端都实现了客户端分区(比如:RedisPool),当然,也可以自己编码是现实。
客户端有很多种计算key的落地到那个Redis实例的算法,这个时最普通的hash算法。
1.5.1、普通hash算法计算公式:hash(key)%N;hash:可以采用hash算法,比如CRC32、CRC16等;N:是Redis主机个数。
优点
数据分布均匀,实现简单
缺点
节点数量固定,扩展的话需要重新计算hash。
查询必须采用分片的key来查询,一旦key变化了,数据就查询不出来了,所以不要轻易改变key的分区。
1.5.1、一致性hash算法1.5.1.1、算法简介普通hash是对主机数量取模,而一致性hash是对2^32(4 294 967 296)取模(一个足够大的hash表)。
我们把2^32想象成一个圆,就像钟表一样,钟表的圆可以理解成由60个点组成的圆,而此处我们把这个圆想象成由2^32个点组成的圆,示意图如下:
圆环的正上方的点代表0,0点右侧的第一个点代表1,以此类推,2、3、4、5、6……直到2^32-1,也就是说0点左侧的第一个点代表2^32-1 。我们把这个由2的32次方个点组成的圆环称为hash环。
假设我们有3台缓存服务器(A/B/C),使用它们各自的IP地址进行哈希计算,使用哈希后的结果对2^32取模,可以使用如下公式:
hash("服务器的IP地址") % 2^32
通过上述公式算出的结果一定是一个0到2^32-1之间的一个整数,我们就用算出的这个整数,分别代表服务器(A/B/C).
既然这个整数肯定处于0到2^32-1之间,那么,上图中的hash环上必定有一个点与这个整数对应,也就是服务器A/B/C就可以映射到这个环上,如下图:
假设,我们需要使用Redis缓存数据,那么我们使用如下公式可以将数据映射到上图中的hash环上
hash(key) % 2^32
现在服务器与数据都被映射到了hash环上,上图中的数据将会被缓存到服务器A上。
因为从数据的开始位置,沿顺时针方向遇到的第一个服务器就是A服务器,所以,上图中的数据将会被缓存到服务器A上。
1.5.1.2、一致性hash算法有缺点
优点
添加或移除节点时,数据只需要做部分的迁移,比如上图中把C服务器移除,则数据4迁移到服务器A中,而其他的数据保持不变。
缺点
数据分布不均匀,可能出现所有的key都被hash到同一个节点上了,折中现象叫做hash环偏移。
理论上我们可以增加服务器数量来减少便宜,但是这样成本太高了。所以通过增加虚拟节点来处理。
1.5.1.3、一致性hash虚拟节点"虚拟节点"是"实际节点"(实际的物理服务器)在hash环上的复制品,一个实际节点可以对应多个虚拟节点。
从上图可以看出,A、B、C三台服务器分别虚拟出了一个虚拟节点,当然,如果你需要,也可以虚拟出更多的虚拟节点。
引入虚拟节点的概念后,缓存的分布就均衡多了,上图中,1和3号数据被缓存在服务器A中,4和5号数据被缓存在服务器B中,2和6号数据被缓存在服务器C中.
如果你还不放心,可以虚拟出更多的虚拟节点,以便减小hash环偏斜所带来的影响,虚拟节点越多,hash环上的节点就越多,缓存被均匀分布的概率就越大。
一般来说,一致性hash算法 虚拟节点就是一个很好的方案了。
1.5.1.4、一致性hash算法实现一致性hash算法(无虚拟节点)
public class HashDemo {
public static void main(String[] args) {
//step1 初始化:把服务器节点IP的哈希值对应到哈希环上
// 定义服务器ip
String[] servers = new String[]{"192.168.222.101", "192.168.222.102", "192.168.222.103"};
// 创建一个排序的hashMap,key存储hash值,value存储服务器IP地址,并按照Hash值排序
SortedMap<Integer, String> hashServerMap = new TreeMap<>();
for (String redisServer : servers) {
// 求出每⼀个ip的hash值,对应到hash环上,存储hash值与ip的对应关系
int serverHash = Math.abs(redisServer.hashCode());
// 存储hash值与ip的对应关系
hashServerMap.put(serverHash, redisServer);
}
//step2 针对客户端IP求出hash值
// 定义客户端传递过来的RedisKey
String[] redisKeys = new String[]{"user:001:name", "user:001:age", "user:001:sex"};
for (String redisKey : redisKeys) {
// 计算redisKey的hash值
int redisKeyHash = Math.abs(redisKey.hashCode());
//step3 针对客户端,找到能够处理当前RedisKey的服务器(哈希环上顺时针最近)
// 根据redisKey的哈希值去找出哪⼀个服务器节点能够处理
// tailMap返回此映射的键大于或等于fromKey的部分,也就是比redisKey的hash值大的排序列表,取第一个就是最近的服务器节点
SortedMap<Integer, String> filteredSortedMap = hashServerMap.tailMap(redisKeyHash);
// 获取key落到那台服务器,filteredSortrdMap为空,直接取服务器列表hashServerMap第一个,不为空,则取出最近一个filteredSortrdMap
Integer hashKey = filteredSortedMap.isEmpty() ?
hashServerMap.firstKey() : filteredSortedMap.firstKey();
System.out.println("==========>>>>RedisKey:" redisKey " 被路由到服务器:" hashServerMap.get(hashKey));
}
}
}
运行结果
==========>>>>RedisKey:user:001:name 被路由到服务器:192.168.222.101
==========>>>>RedisKey:user:001:age 被路由到服务器:192.168.222.101
==========>>>>RedisKey:user:001:sex 被路由到服务器:192.168.222.101
可以看出,Redis的key被路由到同一个节点了,我们使用增加虚拟节点来避免折中情况。
一致性hash算法(有虚拟节点)
public class HashDemo2 {
public static void main(String[] args) {
//step1 初始化:把服务器节点IP的哈希值对应到哈希环上
// 定义服务器ip
String[] servers = new String[]{"192.167.222.101", "192.168.222.103", "191.169.222.123"};
// 创建一个排序的hashMap,key存储hash值,value存储服务器IP地址,并按照Hash值排序
SortedMap<Integer, String> hashServerMap = new TreeMap<>();
// 定义针对每个真实服务器虚拟出来⼏个节点
int virtualCount = 3;
for (String redisServer : servers) {
// 求出每⼀个ip的hash值,对应到hash环上,存储hash值与ip的对应关系
int serverHash = Math.abs(redisServer.hashCode());
// 存储hash值与ip的对应关系
hashServerMap.put(serverHash, redisServer);
// 处理虚拟节点
for(int i = 0; i < virtualCount; i ) {
int virtualHash = Math.abs((redisServer "#" i).hashCode());
hashServerMap.put(virtualHash,"----由虚拟节点" i "映射过来的请求:" redisServer);
}
}
//step2 针对客户端IP求出hash值
// 定义客户端传递过来的RedisKey
String[] redisKeys = new String[]{
"user:001:name", "order:001:name", "product:001:name",
"user:002:name", "order:002:name", "product:002:name",
};
for (String redisKey : redisKeys) {
// 计算redisKey的hash值
int redisKeyHash = Math.abs(redisKey.hashCode());
//step3 针对客户端,找到能够处理当前RedisKey的服务器(哈希环上顺时针最近)
// 根据redisKey的哈希值去找出哪⼀个服务器节点能够处理
// tailMap返回此映射的键大于或等于fromKey的部分,也就是比redisKey的hash值大的排序列表,取第一个就是最近的服务器节点
SortedMap<Integer, String> filteredSortedMap = hashServerMap.tailMap(redisKeyHash);
// 获取key落到那台服务器,filteredSortrdMap为空,直接取服务器列表hashServerMap第一个,不为空,则取出最近一个filteredSortrdMap
Integer hashKey = filteredSortedMap.isEmpty() ?
hashServerMap.firstKey() : filteredSortedMap.firstKey();
System.out.println("==========>>>>RedisKey:" redisKey " 被路由到服务器:" hashServerMap.get(hashKey));
}
}
}
运行结果
==========>>>>RedisKey:user:001:name 被路由到服务器:192.167.222.101
==========>>>>RedisKey:order:001:name 被路由到服务器:191.169.222.123
==========>>>>RedisKey:product:001:name 被路由到服务器:191.169.222.123
==========>>>>RedisKey:user:002:name 被路由到服务器:----由虚拟节点2映射过来的请求:191.169.222.123
==========>>>>RedisKey:order:002:name 被路由到服务器:191.169.222.123
==========>>>>RedisKey:product:002:name 被路由到服务器:191.169.222.123
可以看出,虚拟节点确实生效了。
2、Redis集群Redis3.0之后,Redis官方提供了完整的集群解决方案,称为Rediscluster。
Redis集群方案采用去中心化的方式,包括:sharding(分区)、replication(复制)、failover(故障转移)。
Redis5.0前采用redis-trib进行集群的创建和管理,需要ruby支持。Redis5.0可以直接使用Redis-cli进行集群的创建和管理。
2.1、Redis集群部署架构
Redis的部署架构时没有中心的,每个节点都是主节点,是一个P2P(点对点)的去中心化集群架构,依靠gossip协议用于集群间传播。
2.2、Gossip协议Gossip协议是一个通信协议,一种传播消息的方式,它起源于病毒传播。
Gossip协议基本思想
一个节点周期性(每秒)随机选择一些节点,并把信息传递给这些节点。
这些收到信息的节点接下来会做同样的事情,即把这些信息传递给其他一些随机选择的节点。
信息会周期性的传递给N个目标节点。这个N被称为fanout(扇出)。
gossip协议包含多种消息,包括meet、ping、pong、fail、publish等等。
- 1、meet:sender向receiver发出,请求receiver加入sender的集群(加入集群时的协议)
- 2、ping:节点检测其他节点是否在线
- 3、pong:receiver收到meet或ping后的回复信息;在failover后,新的Master也会广播pong
- 4、fail:节点A判断节点B下线后,A节点广播B的fail信息,其他收到节点会将B节点标记为下线
- 5、节点A收到publish命令,执行该命令,并向集群广播publish命令,收到publish命令的节点都会执行相同的publish命令
通过gossip协议,cluster可以提供集群间状态同步更新、自动选举,故障转移(failover)等重要的集群功能.
2.3、Redis的hash槽redis-cluster把所有的物理节点映射到[0-16383]个slot上,基本上采用平均分配和连续分配的方式。
比如部署架构图中有5个主节点,这样在RedisCluster创建时,slot槽可按如下分配:
Redis1 0-3270, Redis2 3271-6542, Redis3 6543-9814, Redis4 9815-13087, Redis5 13088-16383
cluster负责维护节点和slot槽的对应关系 value------>slot-------->节点
当需要在Redis集群中放置一个key-value时,redis先对key使用crc16算法算出一个结果,然后把结果对16384求余数,这样每个key都会对应一个编号在0-16383之间的哈希槽,redis会根据节点数量大致均等的将哈希槽映射到不同的节点。
2.5、RedisCluster的优点- 1、高性能:Redis的性能和单机版时同级别的,但是存在多个主节点,可以做负载均衡,读写分离等。
- 2、高可用:Redis分片集群的同时,对每个节点都可以采用主从复制保障每个节点的高可用。
- 3、故障转移,Redis也实现了一个类似Raft的方式,保证整个集群的可用性。failover故障自动转移。
- 4、易扩展:向Redis增加,移除节点是透明的,无需停机。水平和垂直方向都非常容易扩展。
- 5、Redis官方实现:部署RedisCluster不需要其他代理或者工具,使用Redis-Cli即可。是Redis官方开发的,与单机版几乎完全兼容。
生产环境中的Redis服务器最少三台主服务器,三台从服务器。这里由于条件有限,在同一台机器上处理,也就是实现伪分布式集群。
下载Redis
下载地址:https://download.redis.io/releases/redis-5.0.0.tar.gz
安在Redis单机版
请查看<<Redis主从复制 哨兵选举机制分析>> 中,地址为:https://www.toutiao.com/article/7140648630402204168
准备工作
# 创建redis集群文件夹
mkdir -p /opt/redis/redis_cluster
# 复制一份编译好的Redis命令
cp -r /opt/redis/baseredis /opt/redis/redis_cluster/redis1
# 复制配置文件
cp /opt/redis/redis-5.0.0/redis.conf /opt/redis/redis_cluster/redis1/redis.conf
# 复制六份,分别修改配置文件
cp -r redis1 redis2 ; cp -r redis1 redis3 ; cp -r redis1 redis4 ; cp -r redis1 redis5 ; cp -r redis1 redis6
redis.conf配置文件修改
################################## NETWORK #####################################
# 注释掉bind 127.0.0.1,不然ip地址只能使用127.0.0.1访问
# bind 127.0.0.1
# 端口号(需要修改)
port 7001
tcp-backlog 511
timeout 0
tcp-keepalive 300
################################# GENERAL #####################################
# 开启后台启动(修改为yes)
daemonize yes
supervised no
# 进程ID(修改为对应端口号的pid)
pidfile /var/run/redis_7001.pid
loglevel notice
logfile ""
databases 16
always-show-logo yes
################################ SNAPSHOTTING ################################
save 900 1
save 300 10
save 60 10000
stop-writes-on-bgsave-error yes
rdbchecksum yes
dbfilename dump.rdb
dir ./
################################# REPLICATION #################################
# 主从复制
# replicaof <masterip> <masterport>
# 连接主服务器认证密码(修改为集群的密码,每台机器都一样)
masterauth abcAbc123.
replica-serve-stale-data yes
replica-read-only yes
repl-diskless-sync no
repl-disable-tcp-nodelay no
# By default the priority is 100.
replica-priority 100
################################## SECURITY ###################################
# 认证密码(修改为集群的密码,每台机器都一样)
requirepass abcAbc123.
############################# LAZY FREEING ####################################
lazyfree-lazy-eviction no
lazyfree-lazy-expire no
lazyfree-lazy-server-del no
replica-lazy-flush no
############################## APPEND ONLY MODE ###############################
# 是否开启AOF(修改为yes, 开启aof)
appendonly yes
# 开启aof后的文件名
appendfilename "appendonly.aof"
# appendfsync always
appendfsync everysec
# appendfsync no
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 SCRIPTING ###############################
# Set it to 0 or a negative value for unlimited execution without warnings.
lua-time-limit 5000
################################ REDIS CLUSTER ###############################
# 是否开启集群(修改为yes,开启集群功能)
cluster-enabled yes
# 集群节点的配置文件(修改为对应端口号的集群端口号对用节点)
cluster-config-file nodes-7001.conf
# 节点间通信超时时间
cluster-node-timeout 15000
################################## SLOW LOG ###################################
slowlog-max-len 128
################################ LATENCY MONITOR ##############################
latency-monitor-threshold 0
############################# EVENT NOTIFICATION ##############################
notify-keyspace-events ""
############################### ADVANCED CONFIG ###############################
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
同样的配置复制6份,修改为端口号相关的东西,比如端口号,进程ID文件,集群节点名称等。可以使用以上配置批量替换
vi编辑器中执行
# 将7001替换成7002 /g表示全部替换
:%s/7001/7002/g
启动Redis
cd /opt/redis/redis_cluster/redis1/ && ./bin/redis-server redis.conf
cd /opt/redis/redis_cluster/redis2/ && ./bin/redis-server redis.conf
cd /opt/redis/redis_cluster/redis3/ && ./bin/redis-server redis.conf
cd /opt/redis/redis_cluster/redis4/ && ./bin/redis-server redis.conf
cd /opt/redis/redis_cluster/redis5/ && ./bin/redis-server redis.conf
cd /opt/redis/redis_cluster/redis6/ && ./bin/redis-server redis.conf
查看redis启动情况
[root@VM-0-5-centos redis6]# ps -ef |grep redis
root 1485 1 0 22:22 ? 00:00:00 ./bin/redis-server *:7001 [cluster]
root 1604 1 0 22:22 ? 00:00:00 ./bin/redis-server *:7002 [cluster]
root 1606 1 0 22:22 ? 00:00:00 ./bin/redis-server *:7003 [cluster]
root 1611 1 0 22:22 ? 00:00:00 ./bin/redis-server *:7004 [cluster]
root 1617 1 0 22:22 ? 00:00:00 ./bin/redis-server *:7005 [cluster]
root 1630 1 0 22:22 ? 00:00:00 ./bin/redis-server *:7006 [cluster]
开启防火墙
firewall-cmd --zone=public --add-port=7001/tcp --permanent
firewall-cmd --zone=public --add-port=7002/tcp --permanent
firewall-cmd --zone=public --add-port=7003/tcp --permanent
firewall-cmd --zone=public --add-port=7004/tcp --permanent
firewall-cmd --zone=public --add-port=7005/tcp --permanent
firewall-cmd --zone=public --add-port=7006/tcp --permanent
firewall-cmd --zone=public --add-port=17001/tcp --permanent
firewall-cmd --zone=public --add-port=17002/tcp --permanent
firewall-cmd --zone=public --add-port=17003/tcp --permanent
firewall-cmd --zone=public --add-port=17004/tcp --permanent
firewall-cmd --zone=public --add-port=17005/tcp --permanent
firewall-cmd --zone=public --add-port=17006/tcp --permanent
systemctl restart firewalld.service
放行云服务器的防火墙端口(7001-7006,17001-17006)于本机公网IP
开启集群(公网IP,指定密码)
/opt/redis/redis_cluster/redis1/bin/redis-cli --cluster create \
162.14.74.11:7001 \
162.14.74.11:7002 \
162.14.74.11:7003 \
162.14.74.11:7004 \
162.14.74.11:7005 \
162.14.74.11:7006 \
--cluster-replicas 1 -a abcAbc123.
- 1、 --cluster-replicas表示副本数量为1,六台Redis实例,复本为1,也就是三主三从。
- 2、 -a表示指定密码
加入集群结果
[root@VM-0-5-centos redis6]# /opt/redis/redis_cluster/redis1/bin/redis-cli --cluster create \
> 162.14.74.11:7001 \
> 162.14.74.11:7002 \
> 162.14.74.11:7003 \
> 162.14.74.11:7004 \
> 162.14.74.11:7005 \
> 162.14.74.11:7006 \
> --cluster-replicas 1 -a abcAbc123.
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
>>> Performing hash slots allocation on 6 nodes...
Master[0] -> Slots 0 - 5460
Master[1] -> Slots 5461 - 10922
Master[2] -> Slots 10923 - 16383
Adding replica 162.14.74.11:7004 to 162.14.74.11:7001
Adding replica 162.14.74.11:7005 to 162.14.74.11:7002
Adding replica 162.14.74.11:7006 to 162.14.74.11:7003
>>> Trying to optimize slaves allocation for anti-affinity
[WARNING] Some slaves are in the same host as their master
M: 95bb2b273537ec44879178d80fec968a4a02d151 162.14.74.11:7001
slots:[0-5460] (5461 slots) master
M: 840c4b75f4603e1e1baa3189154adc9c2dc9abc7 162.14.74.11:7002
slots:[5461-10922] (5462 slots) master
M: 478f97fc6bc954daadc1aeed49bd90b5b8f921a9 162.14.74.11:7003
slots:[10923-16383] (5461 slots) master
S: b7cefab8fbce66cf7784b52a18c1d07fbc485e8d 162.14.74.11:7004
replicates 95bb2b273537ec44879178d80fec968a4a02d151
S: 9d6cc857350689586336dfd6d82f0fbb41dd8450 162.14.74.11:7005
replicates 840c4b75f4603e1e1baa3189154adc9c2dc9abc7
S: 830c13e63211ef4c4dc2668581895f78b45ff06c 162.14.74.11:7006
replicates 478f97fc6bc954daadc1aeed49bd90b5b8f921a9
Can I set the above configuration? (type 'yes' to accept): yes # 这里输入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 162.14.74.11:7001)
M: 95bb2b273537ec44879178d80fec968a4a02d151 162.14.74.11:7001
slots:[0-5460] (5461 slots) master
1 additional replica(s)
S: 830c13e63211ef4c4dc2668581895f78b45ff06c 162.14.74.11:7006
slots: (0 slots) slave
replicates 478f97fc6bc954daadc1aeed49bd90b5b8f921a9
M: 478f97fc6bc954daadc1aeed49bd90b5b8f921a9 162.14.74.11:7003
slots:[10923-16383] (5461 slots) master
1 additional replica(s)
M: 840c4b75f4603e1e1baa3189154adc9c2dc9abc7 162.14.74.11:7002
slots:[5461-10922] (5462 slots) master
1 additional replica(s)
S: b7cefab8fbce66cf7784b52a18c1d07fbc485e8d 162.14.74.11:7004
slots: (0 slots) slave
replicates 95bb2b273537ec44879178d80fec968a4a02d151
S: 9d6cc857350689586336dfd6d82f0fbb41dd8450 162.14.74.11:7005
slots: (0 slots) slave
replicates 840c4b75f4603e1e1baa3189154adc9c2dc9abc7
[OK] All nodes agree about slots configuration. # 加入集群成功
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
连接Redis进行测试
# 使用集群方式连接Redis
/opt/redis/redis_cluster/redis1/bin/redis-cli -h 162.14.74.11 -p 7001 -c
# 使用密码认证
162.14.74.11:7001> auth abcAbc123.
OK
# 使用cluster nodes查询集群节点
162.14.74.11:7001> cluster nodes
830c13e63211ef4c4dc2668581895f78b45ff06c 162.14.74.11:7006@17006 slave 478f97fc6bc954daadc1aeed49bd90b5b8f921a9 0 1662994023610 6 connected
95bb2b273537ec44879178d80fec968a4a02d151 10.0.0.5:7001@17001 myself,master - 0 1662994021000 1 connected 0-5460
478f97fc6bc954daadc1aeed49bd90b5b8f921a9 162.14.74.11:7003@17003 master - 0 1662994022610 3 connected 10923-16383
840c4b75f4603e1e1baa3189154adc9c2dc9abc7 162.14.74.11:7002@17002 master - 0 1662994024612 2 connected 5461-10922
b7cefab8fbce66cf7784b52a18c1d07fbc485e8d 162.14.74.11:7004@17004 slave 95bb2b273537ec44879178d80fec968a4a02d151 0 1662994022000 4 connected
9d6cc857350689586336dfd6d82f0fbb41dd8450 162.14.74.11:7005@17005 slave 840c4b75f4603e1e1baa3189154adc9c2dc9abc7 0 1662994023000 5 connected
使用Docker集群也是一样的道理。我们使用host模式(直接使用本机IP),实现集群。
准备工作
# 创建docker方式redis集群文件夹
mkdir -p /opt/redis/docker_redis_cluster
# 拷贝准备好的文件到该目录
# 如果没有安装tree 先安装
yum install tree -y
使用tree查看目录结构, 只需要创建配置文件目录进行挂载修改,data和logs会自动挂载。
.
├── docker-compose.yml
├── redis1
│ └── conf
│ └── redis.conf
├── redis2
│ └── conf
│ └── redis.conf
├── redis3
│ └── conf
│ └── redis.conf
├── redis4
│ └── conf
│ └── redis.conf
├── redis5
│ └── conf
│ └── redis.conf
└── redis6
└── conf
└── redis.conf
再使用ls -al查询隐藏文件
[root@VM-0-5-centos docker_redis_cluster]# ls -al
total 40
drwxr-xr-x 8 root root 4096 Sep 14 21:29 .
drwxr-xr-x 8 root root 4096 Sep 12 22:49 ..
-rw-r--r-- 1 root root 2953 Sep 14 21:29 docker-compose.yml
-rw-r--r-- 1 root root 24 Sep 13 21:23 .env
drwxr-xr-x 3 root root 4096 Sep 14 21:31 redis1
drwxr-xr-x 3 root root 4096 Sep 14 21:31 redis2
drwxr-xr-x 3 root root 4096 Sep 14 21:31 redis3
drwxr-xr-x 3 root root 4096 Sep 14 21:31 redis4
drwxr-xr-x 3 root root 4096 Sep 14 21:31 redis5
drwxr-xr-x 3 root root 4096 Sep 14 21:31 redis6
可以看到我们准备了docker-compose.yml, .env, redis.conf三种文件。只需要修改.env的变量,就可以通过docker-compose实现一键集群了。
.env
# 服务器的IP地址 公网IP 注意开启防火墙端口 和 公网的安全组
SERVICE_IP=162.14.74.11
# 根目录
BASE_DIR=/opt/redis/docker_redis_cluster
编写docker-compose.yml
version: '2'
services:
redis_cluster:
image: redis
hostname: redis_cluster
container_name: redis_cluster
command: 'redis-cli --cluster create ${SERVER_IP}:7001 ${SERVER_IP}:7002 ${SERVER_IP}:7003 ${SERVER_IP}:7004 ${SERVER_IP}:7005 ${SERVER_IP}:7006 --cluster-yes --cluster-replicas 1 -a abcAbc123.'
depends_on:
- redis1
- redis2
- redis3
- redis4
- redis5
- redis6
privileged: true
environment:
TZ: Asia/Shanghai
network_mode: host
redis1:
# 镜像名
image: redis
# 重启策略 失败后总是重启
restart: always
# 主机名
hostname: redis1
# 容器名
container_name: redis1
# 是否有权限 true
privileged: true
# 网络模式 host表示与宿主机使用相同的IP,docker中的IP发生变化
network_mode: host
environment:
TZ: Asia/Shanghai
volumes:
- ${BASE_DIR}/redis1/data:/data
- ${BASE_DIR}/redis1/conf/redis.conf:/etc/redis/redis.conf
- ${BASE_DIR}/redis1/logs:/logs
command: [ "redis-server","/etc/redis/redis.conf" ]
redis2:
image: redis
restart: always
hostname: redis2
container_name: redis2
privileged: true
network_mode: host
environment:
TZ: Asia/Shanghai
volumes:
- ${BASE_DIR}/redis2/data:/data
- ${BASE_DIR}/redis2/conf/redis.conf:/etc/redis/redis.conf
- ${BASE_DIR}/redis2/logs:/logs
command: [ "redis-server","/etc/redis/redis.conf" ]
redis3:
image: redis
restart: always
hostname: redis3
container_name: redis3
privileged: true
network_mode: host
environment:
TZ: Asia/Shanghai
volumes:
- ${BASE_DIR}/redis3/data:/data
- ${BASE_DIR}/redis3/conf/redis.conf:/etc/redis/redis.conf
- ${BASE_DIR}/redis3/logs:/logs
command: [ "redis-server","/etc/redis/redis.conf" ]
redis4:
image: redis
restart: always
hostname: redis4
container_name: redis4
privileged: true
network_mode: host
environment:
TZ: Asia/Shanghai
volumes:
- ${BASE_DIR}/redis4/data:/data
- ${BASE_DIR}/redis4/conf/redis.conf:/etc/redis/redis.conf
- ${BASE_DIR}/redis4/logs:/logs
command: [ "redis-server","/etc/redis/redis.conf" ]
redis5:
image: redis
restart: always
hostname: redis5
container_name: redis5
privileged: true
network_mode: host
environment:
TZ: Asia/Shanghai
volumes:
- ${BASE_DIR}/redis5/data:/data
- ${BASE_DIR}/redis5/conf/redis.conf:/etc/redis/redis.conf
- ${BASE_DIR}/redis5/logs:/logs
command: [ "redis-server","/etc/redis/redis.conf" ]
redis6:
image: redis
restart: always
hostname: redis6
container_name: redis6
privileged: true
network_mode: host
environment:
TZ: Asia/Shanghai
volumes:
- ${BASE_DIR}/redis6/data:/data
- ${BASE_DIR}/redis6/conf/redis.conf:/etc/redis/redis.conf
- ${BASE_DIR}/redis6/logs:/logs
command: [ "redis-server","/etc/redis/redis.conf" ]
redis.conf配置文件
protected-mode yes
port 7001
tcp-backlog 511
timeout 0
tcp-keepalive 300
daemonize no
supervised no
pidfile /var/run/redis_7001.pid
loglevel verbose
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 ./
masterauth abcAbc123.
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
requirepass abcAbc123.
lazyfree-lazy-eviction no
lazyfree-lazy-expire no
lazyfree-lazy-server-del no
replica-lazy-flush no
appendonly yes
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
cluster-enabled yes
cluster-config-file nodes-7001.conf
cluster-node-timeout 15000
cluster-replica-validity-factor 10
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
上面时原始的redis.conf去除了注释后,并修改了部分属性,修改部分如下:
# 端口号
port 7001
# 进程ID保存文件
pidfile /var/run/redis_7001.pid
# 向主服务器认证的密码
masterauth abcAbc123.
# 服务器自己的密码 与向主服务器认证密码保持一致
requirepass abcAbc123.
# 开启appendaof模式
appendonly yes
# 开启集群
cluster-enabled yes
# 集群节点配置文件名称
cluster-config-file nodes-7001.conf
启动集群
cd /opt/redis/docker_redis_cluster && docker-compose up -d
测试集群
# 进入容器
docker exec -it redis1 /bin/bash
# 使用集群方式连接Redis
redis-cli -h 162.14.74.11 -p 7001 -c
# 使用密码认证
162.14.74.11:7001> auth abcAbc123.
OK
# 使用cluster nodes查询集群节点
162.14.74.11:7001> cluster nodes
ef24f98f9d811539ed3aa7ebd58b42c02ae11c1a 162.14.74.11:7005@17005 slave d77367d7830b4503980a14e379cb06a271906787 0 1663164275000 1 connected
5a92ba294e35f1d61e8091216c59c2fa44adfb9d 162.14.74.11:7002@17002 master - 0 1663164276864 2 connected 5461-10922
d77367d7830b4503980a14e379cb06a271906787 10.0.0.5:7001@17001 myself,master - 0 1663164274000 1 connected 0-5460
448b264edf1f697128395d8b6a656129024e2b55 162.14.74.11:7006@17006 slave 5a92ba294e35f1d61e8091216c59c2fa44adfb9d 0 1663164275861 2 connected
841ea8438a496f02c3866fa71fc9d4271b94e946 162.14.74.11:7004@17004 slave 851c1b20f0616b60f172837e7e4bce792397d8ac 0 1663164275000 3 connected
851c1b20f0616b60f172837e7e4bce792397d8ac 162.14.74.11:7003@17003 master - 0 1663164273856 3 connected 10923-16383
使用Docker方式实现集群,其实跟手动实现集群是一样的,只是使用docker-compose一键实现了该操作而已。
2.3、Redis客户端分片与重定向不同节点分组服务于相互无交集的分片(sharding),Redis Cluster不存在单独的proxy或配置服务器,所以需要将客户端路由到目标的分片。
Redis Cluster的客户端相比单机Redis需要具备路由语义的识别能力,且具备一定的路由缓存能力。
2.3.1、moved重定向我们直到,Redis集群数据时存储在各个分片上的,如果我们连接某一个几点,但是数据没有在该节点上,将会被重定向到其他节点获取数据。流程图如下:
流程图
流程说明
- 1、每个节点都共享RedisCluster中槽和集群中对应节点的关系(元数据共享)
- 2、客户端向RedisCluster的任意节点发送命令,接收命令的节点会根据CRC16规则进行hash运算与16384取余,计算自己的槽和对应节点(计算所在槽和节点)
- 3、如果保存数据的槽被分配给当前节点,则去槽中执行命令,并把命令执行结果返回给客户端(数据就在本节点,执行就返回)
- 4、如果保存数据的槽不在当前节点的管理范围内,则向客户端返回moved重定向异常(不在,返回moved重定向标识)
- 5、客户端接收到节点返回的结果,如果是moved异常,则从moved异常中获取目标节点的信息(客户端收到moved重定向)
- 6、客户端向目标节点发送命令,获取命令执行结果(连接目标节点执行命令返回)
命令实现
# 在7001上面设置获取key
root@redis1:/data# redis-cli -h 162.14.74.11 -p 7001 -c
162.14.74.11:7001> get name
# 会经过认证
(error) NOAUTH Authentication required.
162.14.74.11:7001> auth abcAbc123.
OK
# 获取
162.14.74.11:7001> get name
# 获取时发现在7002上,重定向到7002, 但是开启了认证 所有没有获取到
-> Redirected to slot [5798] located at 162.14.74.11:7002
(error) NOAUTH Authentication required.
# 进行认证
162.14.74.11:7002> auth abcAbc123.
OK
# 重新获取则成功
162.14.74.11:7002> get name
"zhangsan"
在对集群进行扩容和缩容时,需要对槽及槽中数据进行迁移
当客户端向某个节点发送命令,节点向客户端返回moved异常,告诉客户端数据对应的槽的节点信息
如果此时正在进行集群扩展或者缩空操作,当客户端向正确的节点发送命令时,槽及槽中数据已经被迁移到别的节点了,就会返回ask,这就是ask重定向机制。
流程图
流程说明
- 1、客户端向目标节点发送命令,目标节点中的槽已经迁移支别的节点上了,此时目标节点会返回ask重定向给客户端。
- 2、客户端向新的节点发送Asking命令给新的节点,然后再次向新节点发送命令
- 3、新节点执行命令,把命令执行结果返回给客户端
- 1、moved重定向,槽已完成了迁移工作
- 2、ask重定向:槽正在迁移的过程中
Redis时可以实现动态扩缩容的,Redis扩容就是向Redis中添加节点
防火墙开通端口
firewall-cmd --zone=public --add-port=7007/tcp --permanent
firewall-cmd --zone=public --add-port=17007/tcp --permanent
systemctl restart firewalld.service
处理这个,还需要开启云服务器的安全组规则。能够telnet通才可以。
[root@VM-0-5-centos docker_redis_cluster]# telnet 162.14.74.11 7007
Trying 162.14.74.11...
Connected to 162.14.74.11.
Escape character is '^]'.
编写增加节点的docker-compose.yml
version: '2'
services:
redis7:
image: redis
restart: always
hostname: redis7
container_name: redis7
privileged: true
network_mode: host
environment:
TZ: Asia/Shanghai
volumes:
- ${BASE_DIR}/redis7/data:/data
- ${BASE_DIR}/redis7/conf/redis.conf:/etc/redis/redis.conf
- ${BASE_DIR}/redis7/logs:/logs
command: [ "redis-server","/etc/redis/redis.conf" ]
执行文件
docker-compose -f addNode.yml up -d
加入集群
在任意一台机器执行如下命令
# 注意 如果设置了密码 加入节点时使用-a指定密码
redis-cli --cluster add-node 162.14.74.11:7007 162.14.74.11:7001 -a abcAbc123.
执行结果
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
>>> Adding node 162.14.74.11:7007 to cluster 162.14.74.11:7001
>>> Performing Cluster Check (using node 162.14.74.11:7001)
M: d77367d7830b4503980a14e379cb06a271906787 162.14.74.11:7001
slots:[0-5460] (5461 slots) master
1 additional replica(s)
S: ef24f98f9d811539ed3aa7ebd58b42c02ae11c1a 162.14.74.11:7005
slots: (0 slots) slave
replicates d77367d7830b4503980a14e379cb06a271906787
M: 5a92ba294e35f1d61e8091216c59c2fa44adfb9d 162.14.74.11:7002
slots:[5461-10922] (5462 slots) master
1 additional replica(s)
S: 448b264edf1f697128395d8b6a656129024e2b55 162.14.74.11:7006
slots: (0 slots) slave
replicates 5a92ba294e35f1d61e8091216c59c2fa44adfb9d
S: 841ea8438a496f02c3866fa71fc9d4271b94e946 162.14.74.11:7004
slots: (0 slots) slave
replicates 851c1b20f0616b60f172837e7e4bce792397d8ac
M: 851c1b20f0616b60f172837e7e4bce792397d8ac 162.14.74.11:7003
slots:[10923-16383] (5461 slots) master
1 additional replica(s)
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
同理,使用cluster nodes查看节点信息
ef24f98f9d811539ed3aa7ebd58b42c02ae11c1a 162.14.74.11:7005@17005 slave d77367d7830b4503980a14e379cb06a271906787 0 1663166631000 1 connected
5a92ba294e35f1d61e8091216c59c2fa44adfb9d 162.14.74.11:7002@17002 master - 0 1663166631175 2 connected 5461-10922
d77367d7830b4503980a14e379cb06a271906787 10.0.0.5:7001@17001 myself,master - 0 1663166628000 1 connected 0-5460
448b264edf1f697128395d8b6a656129024e2b55 162.14.74.11:7006@17006 slave 5a92ba294e35f1d61e8091216c59c2fa44adfb9d 0 1663166630173 2 connected
d5004e076572b8fab64c8cc4473c298396b78823 162.14.74.11:7007@17007 master - 0 1663166632178 0 connected
841ea8438a496f02c3866fa71fc9d4271b94e946 162.14.74.11:7004@17004 slave 851c1b20f0616b60f172837e7e4bce792397d8ac 0 1663166631000 3 connected
851c1b20f0616b60f172837e7e4bce792397d8ac 162.14.74.11:7003@17003 master - 0 1663166630000 3 connected 10923-16383
可以看到,节点已经加入成功了,但是发现不像其他节点有一个connected 0-5460。这是因为还没有分配hash操作,节点上没有数据。
2.4.1、Redis数据迁移 重新分配hash槽添加完主节点需要对主节点进行hash槽分配,这样该主节才可以存储数据。
Redis数据迁移原理
- 1、向节点B发送状态变更命令,将B的对应slot状态置为importing。
- 2、向节点A发送状态变更命令,将A对应的slot状态置为migrating。
- 3、向A发送migrate命令,告知A将要迁移的slot对应的key迁移到B。
- 4、当所有key迁移完成后,cluster setslot重新设置槽位。
# 指定rehash重新分槽的命令
redis-cli --cluster reshard 162.14.74.11:7007 -a abcAbc123.
# 输入hash槽数量,表示要分配hash槽给目标节点
How many slots do you want to move (from 1 to 16384)? 输入3000
# 输入接收槽的结点id 也就是7007节点的id 可以在前面的cluster nodes中获取 d5004e076572b8fab64c8cc4473c298396b78823
What is the receiving node ID? d5004e076572b8fab64c8cc4473c298396b78823
# 输入源结点id,这里输入all表示从其他所有节点中都分一点槽出来
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: all
# 输入yes开始移动槽到目标结点id
Do you want to proceed with the proposed reshard plan (yes/no)? yes
# 这些就是迁移日志 还有很多 只复制了两行
Moving slot 195 from 162.14.74.11:7001 to 162.14.74.11:7007:
Moving slot 196 from 162.14.74.11:7001 to 162.14.74.11:7007:
使用cluster nodes查看
162.14.74.11:7001> cluster nodes
ef24f98f9d811539ed3aa7ebd58b42c02ae11c1a 162.14.74.11:7005@17005 slave d77367d7830b4503980a14e379cb06a271906787 0 1663167613059 7 connected
5a92ba294e35f1d61e8091216c59c2fa44adfb9d 162.14.74.11:7002@17002 master - 0 1663167610000 2 connected 7687-10922
# 7001有两端槽 因为之前迁移错误 迁移到7001去了
d77367d7830b4503980a14e379cb06a271906787 10.0.0.5:7001@17001 myself,master - 0 1663167612000 7 connected 1550-6961 10923-12421
448b264edf1f697128395d8b6a656129024e2b55 162.14.74.11:7006@17006 slave 5a92ba294e35f1d61e8091216c59c2fa44adfb9d 0 1663167611053 2 connected
# 这里7007有i狼三段槽,从7001-7003都迁移了部分过来
d5004e076572b8fab64c8cc4473c298396b78823 162.14.74.11:7007@17007 master - 0 1663167611000 8 connected 0-1549 6962-7686 12422-13146
841ea8438a496f02c3866fa71fc9d4271b94e946 162.14.74.11:7004@17004 slave 851c1b20f0616b60f172837e7e4bce792397d8ac 0 1663167612055 3 connected
851c1b20f0616b60f172837e7e4bce792397d8ac 162.14.74.11:7003@17003 master - 0 1663167608044 3 connected 13147-16383
添加7008从结点,将7008作为7007的从结点
放行防火墙和云服务器安全组
firewall-cmd --zone=public --add-port=7008/tcp --permanent
firewall-cmd --zone=public --add-port=17008/tcp --permanent
systemctl restart firewalld.service
安全组略,需要修改腾讯云上的端口规则。
添加从节点
# redis-cli --cluster add-node 新节点的ip和端口 旧节点ip和端口 --cluster-slave --cluster-master-id 主节点id -a 密码
redis-cli --cluster add-node 162.14.74.11:7008 162.14.74.11:7007 --cluster-slave --cluster-master-id d5004e076572b8fab64c8cc4473c298396b78823 -a abcAbc123.
执行结果
root@redis1:/data# redis-cli --cluster add-node 162.14.74.11:7008 162.14.74.11:7007 --cluster-slave --cluster-master-id d5004e076572b8fab64c8cc4473c298396b78823 -a abcAbc123.
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
>>> Adding node 162.14.74.11:7008 to cluster 162.14.74.11:7007
>>> Performing Cluster Check (using node 162.14.74.11:7007)
M: d5004e076572b8fab64c8cc4473c298396b78823 162.14.74.11:7007
slots:[0-1549],[6962-7686],[12422-13146] (3000 slots) master
S: ef24f98f9d811539ed3aa7ebd58b42c02ae11c1a 162.14.74.11:7005
slots: (0 slots) slave
replicates d77367d7830b4503980a14e379cb06a271906787
S: 841ea8438a496f02c3866fa71fc9d4271b94e946 162.14.74.11:7004
slots: (0 slots) slave
replicates 851c1b20f0616b60f172837e7e4bce792397d8ac
M: 5a92ba294e35f1d61e8091216c59c2fa44adfb9d 162.14.74.11:7002
slots:[7687-10922] (3236 slots) master
1 additional replica(s)
M: 851c1b20f0616b60f172837e7e4bce792397d8ac 162.14.74.11:7003
slots:[13147-16383] (3237 slots) master
1 additional replica(s)
S: 448b264edf1f697128395d8b6a656129024e2b55 162.14.74.11:7006
slots: (0 slots) slave
replicates 5a92ba294e35f1d61e8091216c59c2fa44adfb9d
M: d77367d7830b4503980a14e379cb06a271906787 162.14.74.11:7001
slots:[1550-6961],[10923-12421] (6911 slots) master
1 additional replica(s)
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
>>> Send CLUSTER MEET to node 162.14.74.11:7008 to make it join the cluster.
Waiting for the cluster to join
>>> Configure node as replica of 162.14.74.11:7007.
[OK] New node added correctly.
使用cluster nodes查看
64b8c5798f880927b81ca09d8fc21ab2402767a5 162.14.74.11:7008@17008 slave d5004e076572b8fab64c8cc4473c298396b78823 0 1663168579000 8 connected
ef24f98f9d811539ed3aa7ebd58b42c02ae11c1a 162.14.74.11:7005@17005 slave d77367d7830b4503980a14e379cb06a271906787 0 1663168577000 7 connected
5a92ba294e35f1d61e8091216c59c2fa44adfb9d 162.14.74.11:7002@17002 master - 0 1663168579327 2 connected 7687-10922
d77367d7830b4503980a14e379cb06a271906787 10.0.0.5:7001@17001 myself,master - 0 1663168578000 7 connected 1550-6961 10923-12421
448b264edf1f697128395d8b6a656129024e2b55 162.14.74.11:7006@17006 slave 5a92ba294e35f1d61e8091216c59c2fa44adfb9d 0 1663168580330 2 connected
d5004e076572b8fab64c8cc4473c298396b78823 162.14.74.11:7007@17007 master - 0 1663168579000 8 connected 0-1549 6962-7686 12422-13146
841ea8438a496f02c3866fa71fc9d4271b94e946 162.14.74.11:7004@17004 slave 851c1b20f0616b60f172837e7e4bce792397d8ac 0 1663168579000 3 connected
851c1b20f0616b60f172837e7e4bce792397d8ac 162.14.74.11:7003@17003 master - 0 1663168578000 3 connected 13147-16383
原理与扩容一样
redis-cli --cluster del-node 162.14.74.11:7008 64b8c5798f880927b81ca09d8fc21ab2402767a5 -a abcAbc123.
故障检测
集群中的每个节点都会定期地(每秒)向集群中的其他节点发送PING消息
如果在一定时间内(cluster-node-timeout),发送ping的节点A没有收到某节点B的pong回应,则A将B标识为pfail。
A在后续发送ping时,会带上B的pfail信息, 通知给其他节点。
如果B被标记为pfail的个数大于集群主节点个数的一半(N/2 1)时,B会被标记为fail,A向整个集群广播,该节点已经下线。其他节点收到广播,标记B为fail。
从节点选举
使用raft算法,每个从节点,都根据自己对master复制数据的offset,来设置一个选举时间,offset越大(复制数据越多)的从节点,选举时间越靠前,优先进行选举。
slave通过向其他master发送FAILVOER_AUTH_REQUEST消息发起竞选,master收到后回复FAILOVER_AUTH_ACK消息告知是否同意。
slave发送FAILOVER_AUTH_REQUEST前会将currentEpoch自增,并将最新的Epoch带入到FAILOVER_AUTH_REQUEST消息中,如果自己未投过票,则回复同意,否则回复拒绝。
所有的Master开始slave选举投票,给要进行选举的slave进行投票,如果大部分master node(N/2 1)都投票给了某个从节点,那么选举通过,那个从节点可以切换成master。
RedisCluster失效的判定:
- 1、集群中半数以上的主节点都宕机(无法投票)
- 2、宕机的主节点的从节点也宕机了(slot槽分配不连续)
变更通知
当slave收到过半的master同意时,会成为新的master。此时会以最新的Epoch通过PONG消息广播自己成为master,让Cluster的其他节点尽快的更新拓扑结构(node.conf)
主从切换
从节点通过选举自动切换。
人工处理手动切换
- 1、向从节点发送cluster failover命令
- 2、从节点告知其主节点要进行手动切换(CLUSTERMSG_TYPE_MFSTART)
- 3、主节点会阻塞所有客户端命令的执行(10s)
- 4、从节点从主节点的ping包中获得主节点的复制偏移量
- 5、从节点复制达到偏移量,发起选举、统计选票、赢得选举、升级为主节点并更新配置
- 6、切换完成后,原主节点向所有客户端发送moved指令重定向到新的主节点
如果主节点下线了,则采用cluster failover force或cluster failover takeover进行强制切换。
副本漂移
我们知道在一主一从的情况下,如果主从同时挂了,那整个集群就挂了。
为了避免这种情况我们可以做一主多从,但这样成本就增加了。
Redis提供了一种方法叫副本漂移,这种方法既能提高集群的可靠性又不用增加太多的从机。
Master1宕机,则Slaver11提升为新的Master1
集群检测到新的Master1是单点的(无从机)
集群从拥有最多的从机的节点组(Master3)中,选择节点名称字母顺序最小的从机(Slaver31)漂移到单点的主从节点组(Master1)。具体流程如下(以上图为例):
- 1、将Slaver31的从机记录从Master3中删除
- 2、将Slaver31的的主机改为Master1
- 3、在Master1中添加Slaver31为从节点
- 4、将Slaver31的复制源改为Master1
- 5、通过ping包将信息同步到集群的其他节点
虽然Redis集群操作很简单,但是其内的原理涉及到的知识点非常全面,了解其原理,可以更好的应对线上线下的问题。
4、相关文章本人还写了Redis的其他相关文章,有兴趣的可以点击查看!
- <<Redis主从复制 哨兵选举机制分析>>
- <<Redis弱事务性与Lua脚本原子性分析>>
- <<Redis持久化机制分析>>
- <<Redis的事件处理机制分析>>
- <<Redis客户端和服务端如何通信?>>
- <<Redis的淘汰机制分析>>
- <<Redis的底层数据结构分析>>
- <<Redis的8种数据类型,什么场景使用?>>
- <<缓存是什么?缓存有哪些分类?使用它的代价是什么?>>
- <<缓存的6种常见的使用场景>>
,
免责声明:本文仅代表文章作者的个人观点,与本站无关。其原创性、真实性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容文字的真实性、完整性和原创性本站不作任何保证或承诺,请读者仅作参考,并自行核实相关内容。文章投诉邮箱:anhduc.ph@yahoo.com