redis cluster
redis cluster介绍
redis Custer是redis的分布式解决方案,在3.0版本正式推出
当遇到单机,内存,并发,流量等瓶颈时,可以采用cluster架构方案达到副在均衡目的
redis Cluster之前的分布式方案有两种:
1.客户端分区方案,优点分区逻辑可控,缺点是需要自己处理数据路由,高可用和故障转移等.
2.代理方案,有点是简化客户端分布式逻辑和升级维护便利,缺点加重架构部署性能消耗.
官方提供的redis Cluster集群方案,很好的解决了集群方面的问题
数据分布
分布式数据库首先要解决把整个数据库集按照分区规则映射到多个节点的问题,即把数据集划分到多个节点上,每个节点负责整体数据的一个子集,需要关注的是数据分布规则,redis Cluster采用哈系分片规则.
Redis Cluster 通讯流程
在分布式存储中需要提供维护节点元数据信息的机制,所谓元数据是指:节点负责哪些数据,是否出现故障灯状态信息,redis 集群采用 Gossip(流言)协议,Gossip 协议工作原理就是节点彼此不断交换信息,一段时间后所有的节点都会知道集群完整信息,这种方式类似流言传播。
通信过程:
1)集群中的每一个节点都会单独开辟一个 Tcp 通道,用于节点之间彼此通信,通信端口在基础端口上家10000.
2)每个节点在固定周期内通过特定规则选择结构节点发送 ping 消息
3)接收到 ping 消息的节点用 pong 消息作为响应。集群中每个节点通过一定规则挑选要通信的节点,每个节点可能知道全部节点,也可能仅知道部分节点,只要这些节点彼此可以正常通信,最终他们会打成一致的状态,当节点出现故障,新节点加入,主从角色变化等,它能够给不断的ping/pong消息,从而达到同步目的。
cluster架构
部署cluster
创建相关目录和配置文件
[root@db01 ~]# mkdir -p /opt/redis_cluster/redis_{6380,6381}/{conf,pid,logs} ##创建配置目录
[root@db01 ~]# mkdir -p /data/redis_cluster/redis_{6380,6381} ##创建数据目录
[root@db01 ~]# cat /opt/redis_cluster/redis_6380/conf/redis_6380.conf ##编写配置文件
bind 172.16.210.53
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/"
cluster-enabled yes
cluster-config-file nodes_6380.conf
cluster-node-timeout 15000
EOF
[root@db01 ~]# cd /opt/redis_cluster/
[root@db01 redis_cluster]# cp redis_6380/conf/redis_6380.conf redis_6381/conf/redis_6381.conf ##复制配置文件
[root@db01 redis_cluster]# sed -i 's#6380#6381#g' redis_6381/conf/redis_6381.con ##修改配置文件端口
[root@db01 redis_cluster]# rsync -avz /opt/redis_cluster/redis_638* 172.16.210.54:/opt/redis_cluster/ ##将配置文件同步到54主机
[root@db01 redis_cluster]# rsync -avz /opt/redis_cluster/redis_638* 172.16.210.55:/opt/redis_cluster/ ##将配置文件同步到55主机
配置文件说明
cluster-enabled yes ##激活集群模式
cluster-config-file nodes_6380.conf ##集群的配置文件
cluster-node-timeout 15000 ##集群的超时时间
编写启动脚本
[root@db01 ~]# cat redis_shell.sh
#!/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
[root@db01 ~]# scp redis_shell.sh 172.16.210.54:~
[root@db01 ~]# scp redis_shell.sh 172.16.210.55:~ ##把脚本传给其他主机
脚本用法: 脚本名 执行动作(start|stop|restart) 端口号
启动服务
[root@db01 ~]# bash redis_shell.sh start 6380 ##脚本加执行操作和端口号
root 2944 1 0 6月12 ? 00:03:39 redis-server 172.16.210.53:6379
root 4200 3945 0 09:04 pts/0 00:00:00 bash redis_shell.sh start 6380
root 4208 1 0 09:04 ? 00:00:00 redis-server 172.16.210.53:6380 [cluster]
root 4210 4200 0 09:04 pts/0 00:00:00 grep redis
配置54主机
在54主机上操作
[root@db02 ~]#find /opt/redis_cluster/redis_638* -type f -name "*.conf" | xargs sed -i "/bind/s#53#54#g" ##修改配置文件的地址
[root@db02 conf]# mkdir -p /data/redis_cluster/redis_{6380,6381} ##创建数据目录
[root@db02 ~]# bash redis_shell.sh start 6380 ##启动服务
root 2313 2187 0 09:17 pts/0 00:00:00 bash redis_shell.sh start 6380
root 2321 1 0 09:17 ? 00:00:00 redis-server 172.16.210.54:6380 [cluster]
root 2323 2313 0 09:17 pts/0 00:00:00 grep redis
[root@db02 ~]# bash redis_shell.sh start 6381
root 2321 1 0 09:17 ? 00:00:00 redis-server 172.16.210.54:6380 [cluster]
root 2326 2187 0 09:17 pts/0 00:00:00 bash redis_shell.sh start 6381
root 2334 1 0 09:17 ? 00:00:00 redis-server 172.16.210.54:6381 [cluster]
root 2336 2326 0 09:17 pts/0 00:00:00 grep redis
配置55主机
在55主机上操作
[root@db02 ~]# find /opt/redis_cluster/redis_638* -type f -name "*.conf" | xargs sed -i "/bind/s#53#55#g" ##修改配置文件的地址
[root@db02 ~]# mkdir -p /data/redis_cluster/redis_{6380,6381} ##创建数据目录
[root@db03 ~]# bash redis_shell.sh start 6380 ##启动服务
root 2175 2067 0 09:21 pts/0 00:00:00 bash redis_shell.sh start 6380
root 2183 1 0 09:21 ? 00:00:00 redis-server 172.16.210.55:6381 [cluster]
root 2185 2175 0 09:21 pts/0 00:00:00 grep redis
[root@db03 ~]# bash redis_shell.sh start 6381 ##启动服务
]root 2183 1 0 09:21 ? 00:00:00 redis-server 172.16.210.55:6380 [cluster]
root 2188 2067 0 09:21 pts/0 00:00:00 bash redis_shell.sh start 6380
root 2196 1 0 09:21 ? 00:00:00 redis-server 172.16.210.55:6381 [cluster]
root 2198 2188 0 09:21 pts/0 00:00:00 grep redis
检查3台主机的启动状态
53主机
[root@db01 ~]# ps -ef | grep redis
root 4262 1 0 09:23 ? 00:00:00 redis-server 172.16.210.53:6380 [cluster]
root 4275 1 0 09:23 ? 00:00:00 redis-server 172.16.210.53:6381 [cluster]
root 4291 3945 0 09:23 pts/0 00:00:00 grep --color=auto redis
54主机
[root@db02 ~]# ps -ef | grep redis
root 2321 1 0 09:17 ? 00:00:00 redis-server 172.16.210.54:6380 [cluster]
root 2334 1 0 09:17 ? 00:00:00 redis-server 172.16.210.54:6381 [cluster]
root 2345 2187 0 09:24 pts/0 00:00:00 grep --color=auto redis
55主机
[root@db03 ~]# ps -ef | grep redis
root 2183 1 0 09:21 ? 00:00:00 redis-server 172.16.210.55:6381 [cluster]
root 2196 1 0 09:21 ? 00:00:00 redis-server 172.16.210.55:6380 [cluster]
root 2205 2067 0 09:23 pts/0 00:00:00 grep --color=auto redis
集群命令
查看集群节点
[root@db01 ~]# bash redis_shell.sh login 6380
172.16.210.53:6380> CLUSTER NODES
3eedc5953af49bad9704d307194cd1cd101935f4 :6380 myself,master - 0 0 0 connected
发现集群节点
172.16.210.53:6380> CLUSTER MEET 172.16.210.53 6381
OK
172.16.210.53:6380> CLUSTER MEET 172.16.210.54 6380
OK
172.16.210.53:6380> CLUSTER MEET 172.16.210.54 6381
OK
172.16.210.53:6380> CLUSTER MEET 172.16.210.55 6380
OK
172.16.210.53:6380> CLUSTER MEET 172.16.210.55 6381
OK
##以此把所有节点加入进来
再次使用查看集群节点命令
172.16.210.53:6380> CLUSTER NODES
2c5c48d5c28dd2376b28cf06182ce121d6c62826 172.16.210.54:6380 master - 0 1592117997778 1 connected
10ffb69d3ff475fb3532dda5bce8b9c1d25fc7e2 172.16.210.54:6381 master - 0 1592117993655 5 connected
19196c490965066887e11e206f303bfc8b080616 172.16.210.55:6381 master - 0 1592117994790 4 connected
c10246cc353355edf12063ad0911d39b8b560271 172.16.210.53:6381 myself,master - 0 0 0 connected
3eedc5953af49bad9704d307194cd1cd101935f4 172.16.210.53:6380 master - 0 1592117993654 3 connected
2d304c2691ae2053ed8f943b7e1bca2ad5ced7d8 172.16.210.55:6380 master - 0 1592117992653 2 connected
##此时,所有节点就已经连接成功
分配槽位
虽然节点之间已经互相发现了.但是此时集群还是不可用的状态,因为并没有给节点分配槽位,而却必须是所有的槽位都分配完毕后整个集群才是可用的状态
反之,也就是说只要有一个槽位没有分配,那么整个集群就是不可用的
尝试插入数据
[root@db01 ~]# bash redis_shell.sh login 6380
172.16.210.53:6380> set k1 v1
(error) CLUSTERDOWN Hash slot not served
分配槽位的方法
分配槽位需要在每个主节点上来配置,此时有2中方法执行:
1.分别登录到每个主节点的客户端来执行命令
2.在其中一台机器上用redis客户端远程登录到其他机器的主节点上执行命令
在53节点执行命令
[root@db01 ~]# redis-cli -h 172.16.210.53 -p 6380 cluster addslots {0..5461}
[root@db01 ~]# redis-cli -h 172.16.210.54 -p 6380 cluster addslots {5462..10922}
OK
[root@db01 ~]# redis-cli -h 172.16.210.55 -p 6380 cluster addslots {10923..16383}
分配问所有槽位后我们查看一下集群的状态
[root@db01 ~]# redis-cli -h 172.16.210.53 -p 6380
172.16.210.53: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:3
cluster_stats_messages_sent:1648
cluster_stats_messages_received:1132
可以看到cluster_state
是ok的,证明我们集群状态是ok的
配置高可用
虽然这时候集群是可用的了,但是整个集群只要有一台机器坏掉了,那么整个集群都是不可用的.
所以这时候需要用到其他三个节点分别作为现在三个主节点的从节点,以应对集群主节点故障时可以进行自动切换以保证集群持续可用.
注意:
不要让复制节点复制本机器的主节点, 因为如果那样的话机器挂了集群还是不可用状态, 所以复制节点要复制其他服务器的主节点.
在53节点上操作
注意:
1.需要执行命令的是每个服务器的从节点
2.注意主从的ID不要搞混了
172.16.210.53:6380> CLUSTER NODES
2c5c48d5c28dd2376b28cf06182ce121d6c62826 172.16.210.54:6380 master - 0 1592124516322 1 connected 5462-10922
c10246cc353355edf12063ad0911d39b8b560271 172.16.210.53:6381 master - 0 1592124516471 0 connected
10ffb69d3ff475fb3532dda5bce8b9c1d25fc7e2 172.16.210.54:6381 master - 0 1592124510278 5 connected
19196c490965066887e11e206f303bfc8b080616 172.16.210.55:6381 master - 0 1592124511280 4 connected
3eedc5953af49bad9704d307194cd1cd101935f4 172.16.210.53:6380 myself,master - 0 0 3 connected 0-5461
2d304c2691ae2053ed8f943b7e1bca2ad5ced7d8 172.16.210.55:6380 master - 0 1592124515371 2 connected 10923-16383
[root@db01 ~]# redis-cli -h 172.16.210.53 -p 6381 cluster replicate 2c5c48d5c28dd2376b28cf06182ce121d6c62826
OK
[root@db01 ~]# redis-cli -h 172.16.210.54 -p 6381 cluster replicate 2d304c2691ae2053ed8f943b7e1bca2ad5ced7d8
OK
[root@db01 ~]# redis-cli -h 172.16.210.55 -p 6381 cluster replicate 3eedc5953af49bad9704d307194cd1cd101935f4
OK
插入数据测试:
[root@db01 ~]# redis-cli -h 172.16.210.53 -p 6380
172.16.210.53:6380> set k1 v1
(error) MOVED 12706 172.16.210.55:6380
提示报错:(error) MOVED 12706 172.16.210.55:6380
这是因为集群由于数据被分片了,所以并不是说在那台机器上写入数据就会在那台机器的节点上写入,集群的数据写入和读取就涉及到另外一个概念,ASK路由
Redis Cluster ASK路由
在集群模式下,Redis接受任何键相关命令时首先会计算键对应的槽,再根据槽找出所对应的节点
如果节点是自身,则处理键命令;
否则回复MOVED重定向错误,通知客户端请求正确的节点,这个过程称为Mover重定向.
知道了ask路由后,我们使用-c选项批量插入一些数据
[root@db01 ~]# redis-cli -c -h 172.16.210.53 -p 6380
172.16.210.53:6380> set k4 v4
-> Redirected to slot [8455] located at 172.16.210.54:6380 ##这条信息提示我们数据是插在54主机的
OK
172.16.210.54:6380> keys *
1) "k4"
编写插入脚本测试
[root@db01 ~]# vim for_redis.sh
#!/bin/bash
for i in {1..1000}
do
redis-cli -c -h 172.16.210.54 -p 6380 set k_${i} v_${i}
done
[root@db01 ~]# bash for_redis.sh ##执行插入命令
分别在三台主机查看数据分布情况
主机53
root@db01 ~]# bash redis_shell.sh login 6380
172.16.210.53:6380> keys *
--
---
339) "k_314"
340) "k_365"
341) "k_116" ##插入了341条数据
主机54
[root@db02 ~]# bash redis_shell.sh login 6380
172.16.210.54:6380> keys *
--
--
324) "k_378"
325) "k_534"
326) "k_144"
327) "k_220" ##插入了327条数据
主机55
[root@db03 ~]# bash redis_shell.sh login 6380
172.16.210.55:6380> keys *
--
--
334) "k_980"
335) "k_146"
336) "k_594"
##插入了336条数据
因为有些数据是之前测试插入的,所以这次脚本测试,可以看到数据很平均的插入到了每个节点
故障模拟演练
停掉53主机的主节点
[root@db01 ~]# bash redis_shell.sh stop 6380
root 4730 1 0 16:34 ? 00:00:22 redis-server 172.16.210.53:6381 [cluster]
root 4901 4844 0 20:51 pts/0 00:00:00 redis-cli -c -h 172.16.210.53 -p 6380
root 16036 4924 0 21:29 pts/1 00:00:00 bash redis_shell.sh stop 6380
root 16045 16036 0 21:29 pts/1 00:00:00 grep redis
登录53主机的从节点查看状态
[root@db01 ~]# bash redis_shell.sh login 6381
172.16.210.53:6381> CLUSTER INFO
cluster_state:ok ##可以看到状态还是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:6
cluster_my_epoch:1
cluster_stats_messages_sent:1010
cluster_stats_messages_received:172
可以看到原本53主机的主节点槽位成功转移到了55主机的从节点
172.16.210.53:6381> set czq 19
-> Redirected to slot [2847] located at 172.16.210.55:6381
OK
172.16.210.55:6381> get czq
"19" ##也可以成功插入数据
再次测试:把其他主机的主节点都关闭
因为我们在配置文件中设置了集群的超时时间是15秒,所以每次听掉一个主节点,最好是15秒停一个
[root@db02 ~]# bash redis_shell.sh stop 6380
root 2556 1 0 16:35 ? 00:00:21 redis-server 172.16.210.54:6381 [cluster]
root 2713 2662 0 21:40 pts/1 00:00:00 bash redis_shell.sh stop 6380
root 2722 2713 0 21:40 pts/1 00:00:00 grep redis
[root@db03 ~]# bash redis_shell.sh stop 6380
root 2360 1 0 16:34 ? 00:00:20 redis-server 172.16.210.55:6381 [cluster]
root 2511 2470 0 21:39 pts/1 00:00:00 bash redis_shell.sh stop 6380
root 2520 2511 0 21:39 pts/1 00:00:00 grep redis
查看集群状态
[root@db01 ~]# bash redis_shell.sh login 6381
172.16.210.53:6381> CLUSTER INFO
cluster_state:ok ##状态是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:8
cluster_my_epoch:7
cluster_stats_messages_sent:17008
cluster_stats_messages_received:2007
可以看到槽位也自动转移到了每台主机的从节点
查看数据
[root@db01 ~]# bash redis_shell.sh login 6381
172.16.210.53:6381> keys *
--
--
3315) "k_9465"
3316) "k_8258"
3317) "k_6556"
可以看到原来的数据也还在