Redis高可用Cluster
什么是Redis Cluster
1)Redis集群是一个可以在多个Redis主从复制节点之间进行数据共享的设施(installation)
2)Redis集群不支持那些需要同时处理多个键的Redis命令,因为执行这些命令需要在多个Redis节点之间移动数据,并且在高负载的情况下,这些命令将降低Redis集群的性能,并导致不可预测的行为。
3)Redis集群通过分区(partition)来提供一定程度的可用性(availability):即使集群中有一部分节点失效或者无法进行通讯,集群也可以继续处理命令请求。
4)Redis集群有将数据自动切分(split)到多个节点的能力。
redis Cluster特性
高性能
1.在多酚片节点中,将16384个槽位,均匀分布到多个分片节点中
2.存数据时,将key做crc16(key),然后和16384进行取模,得出槽位值(0-16384之间)
3.根据计算得出的槽位值,找到相对应的分片节点的主节点,存储到相应槽位上
4.如果客户端当时连接的节点不是将来要存储的分片节点,分片集群会将客户端连接切换至真正存储节点进行数据存储
高可用
在搭建集群时,会为每一个分片的主节点,对应一个从节点,实现slaveof功能,同时当主节点down,实现类似于sentinel的自动failover的功能。
redis cluster可以连接任意节点提供服务
如图所示,当我们用客户端连接A分片时,如果按照数据的取模,我们想要访问的数据,不在A分片中,那么集群会自动将请求进行转发。
redis cluster共享数据
Redis 集群使用数据分片(sharding)而非一致性哈希(consistency hashing)来实现: 一个 Redis 集群包含 16384 个哈希槽(hash slot), 数据库中的每个键都属于这 16384 个哈希槽的其中一个, 集群使用公式 CRC16(key) % 16384 来计算键 key 属于哪个槽, 其中 CRC16(key) 语句用于计算键 key 的 CRC16 校验和 。
1.节点 A 负责处理 0 号至 5500 号哈希槽。
2.节点 B 负责处理 5501 号至 11000 号哈希槽。
3.节点 C 负责处理 11001 号至 16384 号哈希槽。
所有的redis节点彼此互联(PING-PONG机制),内部使用二进制协议优化传输速度和带宽.
节点的fail是通过集群中超过半数的master节点检测失效时才生效.
客户端与redis节点直连,不需要中间proxy层.客户端不需要连接集群所有节点,连接集群中任何一个可用节点即可
把所有的物理节点映射到[0-16383]slot上,cluster 负责维护node<->slot<->key
集群如何做复制
为了使得集群在一部分节点下线或者无法与集群的大多数(majority)节点进行通讯的情况下, 仍然可以正常运作, Redis 集群对节点使用了主从复制功能: 集群中的每个节点都有 1 个至 N 个复制品(replica), 其中一个复制品为主节点(master), 而其余的 N-1 个复制品为从节点(slave)。
在之前列举的节点 A 、B 、C 的例子中, 如果节点 B 下线了, 那么集群将无法正常运行, 因为集群找不到节点来处理 5501 号至 11000 号的哈希槽。
假如在创建集群的时候(或者至少在节点 B 下线之前), 我们为主节点 B 添加了从节点 B1 , 那么当主节点 B 下线的时候, 集群就会将 B1 设置为新的主节点, 并让它代替下线的主节点 B , 继续处理 5501 号至 11000 号的哈希槽, 这样集群就不会因为主节点 B 的下线而无法正常运作了。
不过如果节点 B 和 B1 都下线的话, Redis 集群还是会停止运作。
集群的复制特性重用了 SLAVEOF 命令的代码,所以集群节点的复制行为和 SLAVEOF 命令的复制行为完全相同。
Rdis Cluster故障转移
1)在集群里面,节点会对其他节点进行下线检测。
2)当一个主节点下线时,集群里面的其他主节点负责对下线主节点进行故障移。
3)换句话说,集群的节点集成了下线检测和故障转移等类似 Sentinel 的功能。
4)因为 Sentinel 是一个独立运行的监控程序,而集群的下线检测和故障转移等功能是集成在节点里面的,它们的运行模式非常地不同,所以尽管这两者的功能很相似,但集群的实现没有重用 Sentinel 的代码。
redis cluster执行命令的两种情况
1)命令发送到了正确的节点:命令要处理的键所在的槽正好是由接收命令的节点负责,那么该节点执行命令,就像单机 Redis 服务器一样。
2)命令发送到了错误的节点:接收到命令的节点并非处理键所在槽的节点,那么节点将向客户端返回一个转向(redirection)错误,告知客户端应该到哪个节点去执行这个命令,客户端会根据错误提示的信息,重新向正确的节点发送命令。
Redis Cluster部署
环境准备
外网IP | 内网IP | 端口 | 安装包 |
10.0.0.51 | 172.16.1.51 | 7000,7001 | redis-3.2.12.tar.gz |
10.0.0.52 | 172.16.1.52 | 7002,7003 | redis-3.2.12.tar.gz |
10.0.0.53 | 172.16.1.53 | 7004,7005 | redis-3.2.12.tar.gz |
## Redis多实例主从复制
[root@db01 data]# vim 7000/redis.conf
port 7000
daemonize yes
pidfile /data/7000/redis.pid
loglevel notice
logfile "/data/7000/redis.log"
dbfilename dump.rdb
dir /data/7000
protected-mode no
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
appendonly yes
[root@db01 ~]# vim /data/7001/redis.conf
port 7001
daemonize yes
pidfile /data/7001/redis.pid
loglevel notice
logfile "/data/7001/redis.log"
dbfilename dump.rdb
dir /data/7001
protected-mode no
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
appendonly yes
[root@db01 ~]# vim /data/7002/redis.conf
port 7002
daemonize yes
pidfile /data/7002/redis.pid
loglevel notice
logfile "/data/7002/redis.log"
dbfilename dump.rdb
dir /data/7002
protected-mode no
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
appendonly yes
[root@db01 ~]# vim /data/7003/redis.conf
port 7003
daemonize yes
pidfile /data/7003/redis.pid
loglevel notice
logfile "/data/7003/redis.log"
dbfilename dump.rdb
dir /data/7003
protected-mode no
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
appendonly yes
[root@db01 ~]# vim /data/7004/redis.conf
port 7004
daemonize yes
pidfile /data/7004/redis.pid
loglevel notice
logfile "/data/7004/redis.log"
dbfilename dump.rdb
dir /data/7004
protected-mode no
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
appendonly yes
[root@db01 ~]# vim /data/7005/redis.conf
port 7005
daemonize yes
pidfile /data/7005/redis.pid
loglevel notice
logfile "/data/7005/redis.log"
dbfilename dump.rdb
dir /data/7005
protected-mode no
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
appendonly yes
## 启动redis
[root@db01 ~]# redis-server /data/7000/redis.conf
[root@db01 ~]# redis-server /data/7001/redis.conf
[root@db01 ~]# redis-server /data/7002/redis.conf
[root@db01 ~]# redis-server /data/7003/redis.conf
[root@db01 ~]# redis-server /data/7004/redis.conf
[root@db01 ~]# redis-server /data/7005/redis.conf
## 安装redis-cluster插件
# 1.安装ruby环境
[root@db01 ~]# yum install ruby rubygems -y
# 2.更换gem源
[root@db01 ~]# gem source list
*** CURRENT SOURCES ***
https://rubygems.org/
[root@db01 ~]# gem sources --remove https://rubygems.org/
https://rubygems.org/ removed from sources
[root@db01 ~]# gem sources -a http://mirrors.aliyun.com/rubygems/
# 3.安装redis cluster插件
[root@db01 ~]# gem install redis -v 3.3.3
# 4.将节点加入集群
## 老版本
[root@db01 ~]# redis-trib.rb create --replicas 1 127.0.0.1:7000 127.0.0.1:7001 127.0.0.1:7002 127.0.0.1:7003 127.0.0.1:7004 127.0.0.1:7005
## 新版本
[root@db01 ~]# redis-cli --cluster create 127.0.0.1:7000 127.0.0.1:7001 \
127.0.0.1:7002 127.0.0.1:7003 127.0.0.1:7004 127.0.0.1:7005 \
--cluster-replicas 1
>>> Performing hash slots allocation on 6 nodes...
Master[0] -> Slots 0 - 5460
Master[1] -> Slots 5461 - 10922
Master[2] -> Slots 10923 - 16383
Adding replica 127.0.0.1:7004 to 127.0.0.1:7000
Adding replica 127.0.0.1:7005 to 127.0.0.1:7001
Adding replica 127.0.0.1:7003 to 127.0.0.1:7002
>>> Trying to optimize slaves allocation for anti-affinity
[WARNING] Some slaves are in the same host as their master
M: 283059ed91af8b449a74b7d99d37757fa6bb760c 127.0.0.1:7000
slots:[0-5460] (5461 slots) master
M: e921651cb51fb0bd4019d7c10cd14cc3c8951ed3 127.0.0.1:7001
slots:[5461-10922] (5462 slots) master
M: cd3d616715700cf35726ee335ee8528c2880c401 127.0.0.1:7002
slots:[10923-16383] (5461 slots) master
S: c6e46047d2cb32bbc991a7c744153ab934559ff9 127.0.0.1:7003
replicates e921651cb51fb0bd4019d7c10cd14cc3c8951ed3
S: 6ad6ab2ed3854949fdbd9b3abcda770389b0cc3b 127.0.0.1:7004
replicates cd3d616715700cf35726ee335ee8528c2880c401
S: 2ca6109554d000c055fc46be96363101866177fc 127.0.0.1:7005
replicates 283059ed91af8b449a74b7d99d37757fa6bb760c
Can I set the above configuration? (type 'yes' to accept): yes
## 查看master和slave信息
[root@db01 ~]# redis-cli -p 7000 cluster nodes |grep master
283059ed91af8b449a74b7d99d37757fa6bb760c 127.0.0.1:7000@17000 myself,master - 0 1662363861000 1 connected 0-5460
cd3d616715700cf35726ee335ee8528c2880c401 127.0.0.1:7002@17002 master - 0 1662363862000 3 connected 10923-16383
e921651cb51fb0bd4019d7c10cd14cc3c8951ed3 127.0.0.1:7001@17001 master - 0 1662363862505 2 connected 5461-10922
[root@db01 ~]# redis-cli -p 7000 cluster nodes
6ad6ab2ed3854949fdbd9b3abcda770389b0cc3b 127.0.0.1:7004@17004 slave cd3d616715700cf35726ee335ee8528c2880c401 0 1662363893958 3 connected
283059ed91af8b449a74b7d99d37757fa6bb760c 127.0.0.1:7000@17000 myself,master - 0 1662363894000 1 connected 0-5460
cd3d616715700cf35726ee335ee8528c2880c401 127.0.0.1:7002@17002 master - 0 1662363893553 3 connected 10923-16383
e921651cb51fb0bd4019d7c10cd14cc3c8951ed3 127.0.0.1:7001@17001 master - 0 1662363894562 2 connected 5461-10922
2ca6109554d000c055fc46be96363101866177fc 127.0.0.1:7005@17005 slave 283059ed91af8b449a74b7d99d37757fa6bb760c 0 1662363893000 1 connected
c6e46047d2cb32bbc991a7c744153ab934559ff9 127.0.0.1:7003@17003 slave e921651cb51fb0bd4019d7c10cd14cc3c8951ed3 0 1662363894562 2 connected
## 连接集群并set key
[root@db01 data]# redis-cli -c -p 7000
127.0.0.1:7000> set name1 zzzz
-> Redirected to slot [12933] located at 127.0.0.1:7002
OK
127.0.0.1:7002> KEYS *
1) "name1"
127.0.0.1:7002> get name
-> Redirected to slot [5798] located at 127.0.0.1:7001
"1111"
Redis Cluster扩展集群
## 准备两台新节点
[root@db01 data]# vim 7006/redis.conf
port 7006
daemonize yes
pidfile /data/7006/redis.pid
loglevel notice
logfile "/data/7006/redis.log"
dbfilename dump.rdb
dir /data/7006
protected-mode no
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
appendonly yes
[root@db01 data]# vim 7007/redis.conf
port 7007
daemonize yes
pidfile /data/7007/redis.pid
loglevel notice
logfile "/data/7007/redis.log"
dbfilename dump.rdb
dir /data/7007
protected-mode no
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
appendonly yes
## 启动
[root@db01 data]# redis-server /data/7006/redis.conf
[root@db01 data]# redis-server /data/7007/redis.conf
## 将新节点的7006端口加入集群
### 老版本
[root@db01 data]# redis-trib.rb add-node 127.0.0.1:7006 127.0.0.1:7000
### 新版本
[root@db01 data]# redis-cli --cluster add-node 127.0.0.1:7006 127.0.0.1:7000
>>> Adding node 127.0.0.1:7006 to cluster 127.0.0.1:7000
>>> Performing Cluster Check (using node 127.0.0.1:7000)
M: 283059ed91af8b449a74b7d99d37757fa6bb760c 127.0.0.1:7000
slots:[0-5460] (5461 slots) master
1 additional replica(s)
S: 6ad6ab2ed3854949fdbd9b3abcda770389b0cc3b 127.0.0.1:7004
slots: (0 slots) slave
replicates cd3d616715700cf35726ee335ee8528c2880c401
M: cd3d616715700cf35726ee335ee8528c2880c401 127.0.0.1:7002
slots:[10923-16383] (5461 slots) master
1 additional replica(s)
M: e921651cb51fb0bd4019d7c10cd14cc3c8951ed3 127.0.0.1:7001
slots:[5461-10922] (5462 slots) master
1 additional replica(s)
S: 2ca6109554d000c055fc46be96363101866177fc 127.0.0.1:7005
slots: (0 slots) slave
replicates 283059ed91af8b449a74b7d99d37757fa6bb760c
S: c6e46047d2cb32bbc991a7c744153ab934559ff9 127.0.0.1:7003
slots: (0 slots) slave
replicates e921651cb51fb0bd4019d7c10cd14cc3c8951ed3
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
>>> Getting functions from cluster
>>> Send FUNCTION LIST to 127.0.0.1:7006 to verify there is no functions in it
>>> Send FUNCTION RESTORE to 127.0.0.1:7006
>>> Send CLUSTER MEET to node 127.0.0.1:7006 to make it join the cluster.
[OK] New node added correctly.
## 重新分片(重新规划槽位)
### 老版本
[root@db01 ~]# redis-trib.rb reshard 127.0.0.1:7000
### 新版本
[root@db01 data]# redis-cli --cluster reshard 127.0.0.1:7000
# 你要分出多少个槽位
How many slots do you want to move (from 1 to 16384)? 2000
# 接收2000槽位的ID是什么?
What is the receiving node ID? e531933588135d3058cb56c0f1ce4fefcc880a84
# 是不是全部接收
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
# 是按照上面的计划执行
Do you want to proceed with the proposed reshard plan (yes/no)? yes
## 将7007从节点加入7006的主节点
#### 老版本
[root@db01 data]# redis-trib.rb add-node --slave --master-id e531933588135d3058cb56c0f1ce4fefcc880a84 127.0.0.1:7007 127.0.0.1:7000
#### 新版本
[root@db01 data]# redis-cli --cluster add-node --cluster-slave --cluster-master-id e531933588135d3058cb56c0f1ce4fefcc880a84 127.0.0.1:7007 127.0.0.1:7000
Redis Cluster删除节点
# 重新分片
[root@db01 data]# redis-cli --cluster reshard 127.0.0.1:7000
# 需要重新分片多少个slot
How many slots do you want to move (from 1 to 16384)? 3998
# 谁来接收这些槽位7000
What is the receiving node ID? 283059ed91af8b449a74b7d99d37757fa6bb760c
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.
# 源节点:谁要给出这些槽位7006
Source node #1: e531933588135d3058cb56c0f1ce4fefcc880a84
# done结束
Source node #2: done
## 删除节点
[root@db01 data]# redis-cli --cluster del-node 127.0.0.1:7006 e531933588135d3058cb56c0f1ce4fefcc880a84
[root@db01 data]# redis-cli --cluster del-node 127.0.0.1:7007 ec5377320578ee2e88d86688df3cbbf0eb6a8a72
Redis存在的问题
缓存穿透
概念:
访问一个不存在的key,缓存不起作用,请求会穿透到DB,流量大时DB会挂掉。
解决方案:
采用布隆过滤器,使用一个足够大的bitmap,用于存储可能访问的key,不存在的key直接被过滤;
访问key未在DB查询到值,也将空值写进缓存,但可以设置较短过期时间。
缓存雪崩
概念:
大量的key设置了相同的过期时间,导致在缓存在同一时刻全部失效,造成瞬时DB请求量大、压力骤增,引起雪崩。
解决方案:
可以给缓存设置过期时间时加上一个随机值时间,使得每个key的过期时间分布开来,不会集中在同一时刻失效。
缓存击穿
概念:
一个存在的key,在缓存过期的一刻,同时有大量的请求,这些请求都会击穿到DB,造成瞬时DB请求量大、压力骤增。
解决方案:
在访问key之前,采用SETNX(set if not exists)来设置另一个短期key来锁住当前key的访问,访问结束再删除该短期key。