Redis-Cluster采用无中心结构,每个节点保存数据和整个集群状态,每个节点都和其他所有节点连接。
集群概念
- 由多个Redis服务器组成的分布式网络服务集群;
- 集群之中有多个Master主节点,每一个主节点都可读可写;
- 节点之间会互相通信,两两相连;
- Redis集群无中心节点。
Redis存储原理
Redis Cluster 将所有数据划分为 16384 个 slots(槽位),每个节点负责其中一部分槽位。槽位的信息存储于每个节点中。只有master节点会被分配槽位,slave节点不会分配槽位。
当Redis Cluster 的客户端来连接集群时,它也会得到一份集群的槽位配置信息,并将其缓存在客户端本地。这样当客户端要查找某个 key 时,可以直接定位到目标节点。同时因为槽位的信息可能会存在客户端与服务器不一致的情况,还需要纠正机制来实现槽位信息的校验调整。
Cluster 默认会对 key 值使用 crc16 算法进行 hash 得到一个整数值,然后用这个整数值对 16384 进行取模来得到具体槽位。
HASH_SLOT = CRC16(key) % 16384
Moved重定向
- 槽命中:直接返回结果
- 槽不命中:即当前键命令所请求的键不在当前请求的节点中,则当前节点会向客户端发送一个Moved 重定向,客户端根据Moved 重定向所包含的内容找到目标节点,再一次发送命令。
故障转移
Redis集群的主节点内置了类似Redis Sentinel的节点故障检测和自动故障转移功能,当集群中的某个主节点下线时,集群中的其他在线主节点会注意到这一点,并对已下线的主节点进行故障转移。
原理如下:
- slave发现自己的master变为FAIL
- 将自己记录的集群currentEpoch(选举轮次标记)加1,并广播信息给集群中其他节点
- 其他节点收到该信息,只有master响应,判断请求者的合法性,并发送结果
- 尝试选举的slave收集master返回的结果,收到超过半数master的统一后变成新Master
- 广播Pong消息通知其他集群节点。
集群进行故障转移的方法和Redis Sentinel进行故障转移的方法基本一样,不同的是,在集群里面,故障转移是由集群中其他在线的主节点负责进行的,所以集群不必另外使用Redis Sentinel。
集群搭建
- 创建redis-cluster.tmpl文件
一定要配置masterauth,不然会集群节点之间无法通信,造成整个集群的fail
port ${PORT}
requirepass 123456
masterauth 123456
protected-mode no
daemonize no
appendonly yes
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 15000
cluster-announce-ip 10.72.96.97
cluster-announce-port ${PORT}
cluster-announce-bus-port 1${PORT}
- port:节点端口;
- requirepass:添加访问认证;
- masterauth:如果主节点开启了访问认证,从节点访问主节点需要认证;
- protected-mode:保护模式,默认值 yes,即开启。开启保护模式以后,需配置 bind ip 或者设置访问密码;关闭保护模式,外部网络可以直接访问;
- daemonize:是否以守护线程的方式启动(后台启动),默认 no;
- appendonly:是否开启 AOF 持久化模式,默认 no;
- cluster-enabled:是否开启集群模式,默认 no;
- cluster-config-file:集群节点信息文件;
- cluster-node-timeout:集群节点连接超时时间;
- cluster-announce-ip:集群节点 IP,填写宿主机的 IP;
- cluster-announce-port:集群节点映射端口;
- cluster-announce-bus-port:集群节点总线端口。
每个 Redis 集群节点都需要打开两个 TCP 连接。一个用于为客户端提供服务的正常 Redis TCP 端口,例如 6379。还有一个基于 6379 端口加 10000 的端口,比如 16379。
第二个端口用于集群总线,这是一个使用二进制协议的节点到节点通信通道。节点使用集群总线进行故障检测、配置更新、故障转移授权等等。客户端永远不要尝试与集群总线端口通信,与正常的 Redis 命令端口通信即可,但是请确保防火墙中的这两个端口都已经打开,否则 Redis 集群节点将无法通信。
- 创建目录和配置文件
for port in `seq 7000 7005`; do \
mkdir -p ${port}/conf \
&& PORT=${port} envsubst < redis-cluster.tmpl > ${port}/conf/redis.conf \
&& mkdir -p ${port}/data;\
done
上面的 shell for 语句,意思就是循环创建 7000 ~ 7005 相关的目录及文件。
执行完之后会生成如下文件
查看其中的某个配置文件如下:
port 7000
requirepass 123456
masterauth 123456
protected-mode no
daemonize no
appendonly yes
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 15000
cluster-announce-ip 10.72.96.97
cluster-announce-port 7000
cluster-announce-bus-port 17000
- 创建redis容器
方式1: shell for,里面的参数根据自己的实际情况修改。
for port in $(seq 6371 6373); do \
docker run -di --restart always --name redis-${port} --net host \
-v /usr/local/docker-redis/redis-cluster/${port}/conf/redis.conf:/usr/local/etc/redis/redis.conf \
-v /usr/local/docker-redis/redis-cluster/${port}/data:/data \
redis redis-server /usr/local/etc/redis/redis.conf; \
done
方式2: docker-compose.yml
version: '3.4'
services:
node1:
image: redis
container_name: redis-node1
restart: always
ports:
- 7000:7000
- 17000:17000
volumes:
- /Users/qiaochunxiang/Desktop/redis/7000/data:/data
- /Users/qiaochunxiang/Desktop/redis/7000/conf/redis.conf:/usr/local/etc/redis/redis.conf
command:
redis-server /usr/local/etc/redis/redis.conf
node2:
image: redis
container_name: redis-node2
restart: always
ports:
- 7001:7001
- 17001:17001
volumes:
- /Users/qiaochunxiang/Desktop/redis/7001/data:/data
- /Users/qiaochunxiang/Desktop/redis/7001/conf/redis.conf:/usr/local/etc/redis/redis.conf
command:
redis-server /usr/local/etc/redis/redis.conf
node3:
image: redis
container_name: redis-node3
restart: always
ports:
- 7002:7002
- 17002:17002
volumes:
- /Users/qiaochunxiang/Desktop/redis/7002/data:/data
- /Users/qiaochunxiang/Desktop/redis/7002/conf/redis.conf:/usr/local/etc/redis/redis.conf
command:
redis-server /usr/local/etc/redis/redis.conf
node4:
image: redis
container_name: redis-node4
restart: always
ports:
- 7003:7003
- 17003:17003
volumes:
- /Users/qiaochunxiang/Desktop/redis/7003/data:/data
- /Users/qiaochunxiang/Desktop/redis/7003/conf/redis.conf:/usr/local/etc/redis/redis.conf
command:
redis-server /usr/local/etc/redis/redis.conf
node5:
image: redis
container_name: redis-node5
restart: always
ports:
- 7004:7004
- 17004:17004
volumes:
- /Users/qiaochunxiang/Desktop/redis/7004/data:/data
- /Users/qiaochunxiang/Desktop/redis/7004/conf/redis.conf:/usr/local/etc/redis/redis.conf
command:
redis-server /usr/local/etc/redis/redis.conf
node6:
image: redis
container_name: redis-node6
restart: always
ports:
- 7005:7005
- 17005:17005
volumes:
- /Users/qiaochunxiang/Desktop/redis/7005/data:/data
- /Users/qiaochunxiang/Desktop/redis/7005/conf/redis.conf:/usr/local/etc/redis/redis.conf
command:
redis-server /usr/local/etc/redis/redis.conf
创建Redis集群
docker exec -it redis-node1 bash
redis-cli --cluster create 10.72.96.97:7000 10.72.96.97:7001 10.72.96.97:7002 10.72.96.97:7003 10.72.96.97:7004 10.72.96.97:7005 --cluster-replicas 1 -a 123456
根据提示输入yes,最后显示信息如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6K990QtS-1626403664641)(https://note.youdao.com/yws/res/16726/F4586B2ED3F2492F88F306BFC91F8457)]
- 查看集群信息
10.72.96.97:7000> cluster nodes
84a5bb9bd2119ca4786b101d00903f226de2f319 10.72.96.97:7000@17000 myself,master - 0 1625628182000 1 connected 0-5460
504c3ac8a9961610a714e703cdd9a3d1eb9feef8 10.72.96.97:7001@17001 master - 0 1625628184124 2 connected 5461-10922
b9fbb4a7f591edfe0837179669bb7f46e31b454e 10.72.96.97:7004@17004 slave 5ac5fb1735c0fbe98e0f27a9b064db227425f510 0 1625628184000 3 connected
b719f7c746cd0343651a8b7e42ec737b643decf9 10.72.96.97:7003@17003 slave 504c3ac8a9961610a714e703cdd9a3d1eb9feef8 0 1625628184000 2 connected
9a1ed5946f9e1fc8d4f326a9711594cefe4b4aeb 10.72.96.97:7005@17005 slave 84a5bb9bd2119ca4786b101d00903f226de2f319 0 1625628183000 1 connected
5ac5fb1735c0fbe98e0f27a9b064db227425f510 10.72.96.97:7002@17002 master - 0 1625628185138 3 connected 10923-16383
可以看到,7000、7001、7002是master节点,其他的为slave节点。
- 连接redis添加数据
root@914da508f3a6:/data# redis-cli -c -p 7000 -a 123456
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
127.0.0.1:7000> select 0
OK
127.0.0.1:7000> set name qiao
-> Redirected to slot [5798] located at 10.72.96.97:7001
OK
10.72.96.97:7001> set age 20
-> Redirected to slot [741] located at 10.72.96.97:7000
OK
10.72.96.97:7000> set sex nan
OK
10.72.96.97:7000> get age
"20"
10.72.96.97:7000> get name
-> Redirected to slot [5798] located at 10.72.96.97:7001
"qiao"
10.72.96.97:7001> get sex
-> Redirected to slot [2584] located at 10.72.96.97:7000
"nan"
可以看到,不同的信息被存到了不同的redis里面。
- 测试故障转移
先停掉node1 端口7000节点。
docker stop redis-node1
连接7001 node2节点,然后查看集群信息
127.0.0.1:7001> cluster nodes
84a5bb9bd2119ca4786b101d00903f226de2f319 10.72.96.97:7000@17000 master,fail - 1625628296058 1625628291000 1 disconnected
5ac5fb1735c0fbe98e0f27a9b064db227425f510 10.72.96.97:7002@17002 master - 0 1625628341420 3 connected 10923-16383
b719f7c746cd0343651a8b7e42ec737b643decf9 10.72.96.97:7003@17003 slave 504c3ac8a9961610a714e703cdd9a3d1eb9feef8 0 1625628339356 2 connected
b9fbb4a7f591edfe0837179669bb7f46e31b454e 10.72.96.97:7004@17004 slave 5ac5fb1735c0fbe98e0f27a9b064db227425f510 0 1625628340384 3 connected
9a1ed5946f9e1fc8d4f326a9711594cefe4b4aeb 10.72.96.97:7005@17005 master - 0 1625628342454 7 connected 0-5460
504c3ac8a9961610a714e703cdd9a3d1eb9feef8 10.72.96.97:7001@17001 myself,master - 0 1625628341000 2 connected 5461-10922
可以看到,7000端口master服务已经挂掉,7005slave节点变成了master。
- 数据再次获取。
127.0.0.1:7001> get name
"qiao"
127.0.0.1:7001> get age
-> Redirected to slot [741] located at 10.72.96.97:7005
"20"
10.72.96.97:7005> get sex
"nan"
可以看到,数据正常获取。