1.redis集群
Redis Cluster 是 redis的分布式解决方案,在3.0版本正式推出
当遇到单机、内存、并发、流量等瓶颈时,可以采用Cluster架构方案达到负载均衡目的。
Redis Cluster之前的分布式方案有两种:
1)客户端分区方案: 优点分区逻辑可控,缺点是需要自己处理数据路由,高可用和故障转移等。
2)代理方案: 优点是简化客户端分布式逻辑和升级维护便利,缺点加重架构部署和性能消耗。
官方提供的 Redis Cluster集群方案,很好的解决了集群方面的问题
2.数据分布
分布式数据库首先要解决把整个数据库集按照分区规则映射到多个节点的问题,即把数据集划分到多个节点上,
每个节点负责整体数据的一个子集,需要关注的是数据分片规则,Redis Cluster采用哈希分片规则。
3 目录规划
# redis安装目录
/opt/redis_cluster/redis_{PORT}/{conf,logs,pid}
# redis数据目录
/data/redis_cluster/redis_{PORT}/redis_{PORT}.rdb
# redis运维脚本
/root/scripts/redis_shell.sh
4.集群拓扑
redis1 192.168.1.102
redis2 192.168.1.103
redis3 192.168.1.103
5.手动搭建部署集群
思路:
先部署一台服务器上的2个集群节点,
把文件发送到其他主机修改IP地址,启动服务
实现命令:
(1)redis1操作
mkdir -p /opt/redis_cluster/redis_{6380,6381}/{conf,logs,pid}
mkdir -p /data/redis_cluster/redis_{6380,6381}
先安装redis,具体步骤参考上章笔记
cat >/opt/redis_cluster/redis_6380/conf/redis_6380.conf<
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
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
(2)复制redis1的安装和数据目录到redis2、redis3
scp -rp /opt/redis_cluster/ [email protected]:/opt
scp -rp /opt/redis_cluster/ [email protected]:/opt
(3)redis2操作:
cd /opt/redis_cluster/redis
make install
find /opt/redis_cluster/redis_638* -type f -name "*.conf"|xargs sed -i "s#222#154#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
(4)redis3操作
cd /opt/redis_cluster/redis
make install
find /opt/redis_cluster/redis_638* -type f -name "*.conf"|xargs sed -i "s#222#223#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
############################################################################################
1.手动配置节点发现
当把所有节点都启动后查看进程会有cluster的字样
但是登录后执行CLUSTER NODES命令会发现只有每个节点自己的ID,目前集群内的节点
还没有互相发现,所以搭建redis集群我们第一步要做的就是让集群内的节点互相发现.
在执行节点发现命令之前我们先查看一下集群的数据目录会发现有生成集群的配置文件
查看后发现只有自己的节点内容,等节点全部发现后会把所发现的节点ID写入这个文件
集群模式的Redis除了原有的配置文件之外又加了一份集群配置文件.当集群内节点
信息发生变化,如添加节点,节点下线,故障转移等.节点会自动保存集群状态到配置文件.
需要注意的是,Redis自动维护集群配置文件,不需要手动修改,防止节点重启时产生错乱.
节点发现使用命令: CLUSTER MEET {IP} {PORT}
提示:在集群内任意一台机器执行此命令就可以
[root@redis1 ~]# sh redis_shell.sh login 6380
192.168.1.102:6380> CLUSTER MEET 192.168.1.102 6381
OK
192.168.1.102:6380> CLUSTER MEET 192.168.1.103 6380
OK
192.168.1.102:6380> CLUSTER MEET 192.168.1.104 6380
OK
192.168.1.102:6380> CLUSTER MEET 192.168.1.103 6381
OK
192.168.1.102:6380> CLUSTER MEET 192.168.1.104 6381
OK
192.168.1.102:6380> CLUSTER NODES
d03cb38d612802aead8f727b1726a3359c241818 192.168.1.102:6380 myself,master - 0 0 4 connected
67c8128df00b2fa304a41bafbadac25a654f196d 192.168.1.102:6381 master - 0 1562059947079 1 connected
a23ec7d444791a0b258ac454ef15cb4d6ab5abd2 192.168.1.104:6381 master - 0 1562059948087 5 connected
e57807d4d35daaaca05f4a9705e844eab15c7ce8 192.168.1.103:6381 master - 0 1562059949098 0 connected
aa9da67a594dfb357195f12ca4c44001804ee470 192.168.1.104:6380 master - 0 1562059945063 3 connected
5cb6895305520e6a0aa4198a6ea5f2c087530b41 192.168.1.103:6380 master - 0 1562059950108 2 connected
节点都发现完毕后我们再次查看集群配置文件
可以看到,发现到的节点的ID也被写入到了集群的配置文件里
(2)Redis Cluster 通讯流程
在分布式存储中需要提供维护节点元数据信息的机制,所谓元数据是指:节点负责哪些数据,是否出现故障灯状态信息,
redis 集群采用 Gossip(流言)协议,Gossip 协议工作原理就是节点彼此不断交换信息,一段时间后所有的节点都会知道集群完整信息,这种方式类似流言传播。
通信过程:
1)集群中的每一个节点都会单独开辟一个 Tcp 通道,用于节点之间彼此通信,防火墙放行(端口号+10000).
2)每个节点在固定周期内通过特定规则选择结构节点发送 ping 消息
3)接收到 ping 消息的节点用 pong 消息作为响应。集群中每个节点通过一定规则挑选要通信的节点,每个节点可能知道全部节点,也可能仅知道部分节点,
只要这些节点彼此可以正常通信,最终他们会打成一致的状态,当节点出现故障,新节点加入,主从角色变化等,它能够给不断的ping/pong消息,从而达到同步目的。
通讯消息类型:
Gossip
Gossip 协议职责就是信息交换,信息交换的载体就是节点间彼此发送Gossip 消息。
常见 Gossip 消息分为:ping、 pong、 meet、 fail 等
meet
meet 消息:用于通知新节点加入,消息发送者通知接受者加入到当前集群,meet 消息通信正常完成后,接收节点会加入到集群中并进行ping、 pong 消息交换
ping
ping 消息:集群内交换最频繁的消息,集群内每个节点每秒想多个其他节点发送 ping 消息,用于检测节点是否在线和交换彼此信息。
pong
Pong 消息:当接收到 ping,meet 消息时,作为相应消息回复给发送方确认消息正常通信,节点也可以向集群内广播自身的 pong 消息来通知整个集群对自身状态进行更新。
fail
fail 消息:当节点判定集群内另一个节点下线时,回向集群内广播一个fail 消息,其他节点收到 fail 消息之后把对应节点更新为下线状态。
(3)Redis Cluster手动分配槽位
虽然节点之间已经互相发现了,但是此时集群还是不可用的状态,因为并没有给节点分配槽位,而且必须是所有的槽位都分配完毕后整个集群才是可用的状态.
反之,也就是说只要有一个槽位没有分配,那么整个集群就是不可用的.
测试命令:
[root@redis1 ~]# sh redis_shell.sh login 6380
192.168.1.102:6380> set k1 v1
(error) CLUSTERDOWN Hash slot not served
192.168.1.102:6380> CLUSTER INFO
cluster_state:fail
cluster_slots_assigned:0
cluster_slots_ok:0
cluster_slots_pfail:0
cluster_slots_fail:0
cluster_known_nodes:6
cluster_size:0
cluster_current_epoch:5
cluster_my_epoch:4
cluster_stats_messages_sent:1200
cluster_stats_messages_received:1200
192.168.1.102:6380>
前面说了,我们虽然有6个节点,但是真正负责数据写入的只有3个节点,其他3个节点只是作为主节点的从节点,也就是说,只需要分配期中三个节点的槽位就可以了
分配槽位的方法:
分配槽位需要在每个主节点上来配置,此时有2种方法执行:
分别登录到每个主节点的客户端来执行命令
在其中一台机器上用redis客户端远程登录到其他机器的主节点上执行命令
每个节点执行命令:
[root@redis1 ~]# redis-cli -h 192.168.129.222 -p 6380 cluster addslots {0..5461}
OK
[root@redis1 ~]# redis-cli -h 192.168.129.154 -p 6380 cluster addslots {5462..10922}
OK
[root@redis1 ~]# redis-cli -h 192.168.129.223 -p 6380 cluster addslots {10923..16383}
OK
分配完所有槽位之后我们再查看一下集群的节点状态和集群状态
可以看到三个节点都分配了槽位,而且集群的状态是OK的
(4)手动配置集群高可用
虽然这时候集群是可用的了,但是整个集群只要有一台机器坏掉了,那么整个集群都是不可用的.
所以这时候需要用到其他三个节点分别作为现在三个主节点的从节点,以应对集群主节点故障时可以进行自动切换以保证集群持续可用.
注意:
1.不要让复制节点复制本机器的主节点, 因为如果那样的话机器挂了集群还是不可用状态, 所以复制节点要复制其他服务器的主节点.
2.使用redis-trid工具自动分配的时候会出现复制节点和主节点在同一台机器上的情况,需要注意
(5)测试集群
这一次我们采用在一台机器上使用redis客户端远程操作集群其他节点
注意:
1.需要执行命令的是每个服务器的从节点
2.注意主从的ID不要搞混了.
执行命令:
[root@redis1 ~]# redis-cli -h 192.168.129.222 -p 6381 CLUSTER REPLICATE 701efeccb46aa2b97d8b5d3e6bc6bce8f16f52b1 (第二台主机6380的id)
OK
[root@redis1 ~]# redis-cli -h 192.168.129.154 -p 6381 CLUSTER REPLICATE 7c0f8cb00172adb3c6b34a96c7e1967676ea637d (第三台主机6380的id)
OK
[root@redis1 ~]# redis-cli -h 192.168.129.223 -p 6381 CLUSTER REPLICATE 237be3b13304172188cfd865fa2741a087f53e9e (第一台主机6380的id)
OK
(6).Redis Cluster测试集群
我们使用常规插入redis数据的方式往集群里写入数据看看会发生什么
[root@redis1 ~]# redis-cli -h redis1 -p 6380 set k1 v1
(error) MOVED 12706 192.168.1.10:6380
结果提示error, 但是给出了集群另一个节点的地址
那么这条数据到底有没有写入呢? 我们登录这两个节点分别查看
[root@redis1 ~]# redis-cli -h redis3 -p 6380 get k1
(nil)
结果没有,这是因为使用集群后由于数据被分片了,所以并不是说在那台机器上写入数据就会在哪台机器的节点上写入,
集群的数据写入和读取就涉及到了另外一个概念,ASK路由
(7).Redis Cluster ASK路由介绍
在集群模式下,Redis接受任何键相关命令时首先会计算键对应的槽,再根据槽找出所对应的节点
如果节点是自身,则处理键命令;
否则回复MOVED重定向错误,通知客户端请求正确的节点,这个过程称为Mover重定向.
知道了ask路由后,我们使用-c选项批量插入一些数据
[root@redis1 ~]# cat input_key.sh
#!/bin/bash
for i in $(seq 1 1000)
do
redis-cli -c -h 192.168.1.102 -p 6380 set k_${i} v_${i} && echo "set k_${i} is ok"
done
写入后我们同样使用-c选项来读取刚才插入的键值,然后查看下redis会不会帮我们路由到正确的节点上
[root@redis1 ~]# redis-cli -c -h redis1 -p 6380
redis1:6380> get k_1
"v_1"
redis1:6380> get k_100
-> Redirected to slot [5541] located at 192.168.1.103:6380
"v_100"
192.168.1.103:6380> get k_1000
-> Redirected to slot [79] located at 192.168.1.102:6380
"v_1000"
192.168.1.102:6380>
(8).模拟故障转移
至此,我们已经手动的把一个redis高可用的集群部署完毕了, 但是还没有模拟过故障
这里我们就模拟故障,停掉期中一台主机的redis节点,然后查看一下集群的变化
我们使用暴力的kill -9(真实环境禁止使用,建议使用kill或pkill)杀掉 redis2上的redis集群节点,然后观察节点状态
理想情况应该是redis1上的6381从节点升级为主节点
在redis1上查看集群节点状态
虽然我们已经测试了故障切换的功能,但是节点修复后还是需要重新上线
所以这里测试节点重新上线后的操作
重新启动redis2的6380,然后观察日志
观察redis1上的日志
这时假如我们想让修复后的节点重新上线,可以在想变成主库的从库执行CLUSTER FAILOVER命令
这里我们在redis2的6380上执行
操作命令:
中止redis2的服务:pkill redis-server
查看日志:sh redis_shell.sh tail 6380
再启动redis2的服务
redis2重回master:redis-cli -h 192.168.129.154 -p 6380 CLUSTER FAILOVER
######################################################################################################
1.使用工具自动搭建部署Redis Cluster
手动搭建集群便于理解集群创建的流程和细节,不过手动搭建集群需要很多步骤,当集群节点众多时,必然会加大搭建集群的复杂度和运维成本,
因此官方提供了 redis-trib.rb的工具方便我们快速搭建集群。redis-trib.rb是采用 Ruby 实现的 redis 集群管理工具,内部通过 Cluster相
关命令帮我们简化集群创建、检查、槽迁移和均衡等常见运维操作,使用前要安装 ruby 依赖环境
前提准备:
停掉所有的节点,然后清空数据,恢复成一个全新的集群,所有机器执行命令
pkill redis
rm -rf /data/redis_cluster/redis_6380/*
rm -rf /data/redis_cluster/redis_6381/*
全部清空之后启动所有的节点,所有机器执行
redis-server /opt/redis_cluster/redis_6380/conf/redis_6380.conf
redis-server /opt/redis_cluster/redis_6381/conf/redis_6381.conf
(1)安装命令:注意新版本redis不需安装,直接采用步骤(2)
yum makecache fast
yum install rubygems
gem sources --remove https://rubygems.org/
gem sources -a http://mirrors.aliyun.com/rubygems/
gem update –system
gem install redis -v 3.3.5
redis1执行创建集群命令
cd /opt/redis_cluster/redis/src/
./redis-trib.rb create --replicas 1 192.168.1.102:6380 192.168.1.103:6380 192.168.1.103:6380 192.168.1.102:6381 192.168.1.104:6381 192.168.1.104:6381
检查集群完整性
./redis-trib.rb check 192.168.1.102:6380
(2)或直接使用redis-cli命令*****
创建群集:
redis-cli --cluster create --cluster-replicas 1 192.168.129.222:6380 192.168.129.154:6380 192.168.129.223:6380 192.168.129.222:6381 192.168.129.154:6381 192.168.129.223:6381
检查完整性:
redis-cli --cluster check 192.168.1.102:6380
################################################################################################
工具扩容节点
Redis集群的扩容操作可分为以下几个步骤
1)准备新节点
2)加入集群
3)迁移槽和数据
扩容流程图:
我们在redis1上创建2个新节点
mkdir -p /opt/redis_cluster/redis_{6390,6391}/{conf,logs,pid}
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
启动节点
bash redis_shell.sh start 6390
bash redis_shell.sh start 6391
发现节点
redis-cli -c -h redis1 -p 6380 cluster meet 192.168.1.102 6390
redis-cli -c -h redis1 -p 6380 cluster meet 192.168.1.102 6391
在redis1上使用工具扩容:注意新版本使用步骤(2)
(1)旧版本3...
cd /opt/redis_cluster/redis/src/
./redis-trib.rb add-node 192.168.1.102:6390 192.168.1.102:6380
./redis-trib.rb add-node 192.168.1.102:6390 192.168.1.102:6380
./redis-trib.rb reshard 192.168.1.102:6380
(2)或直接使用哦redis-cli命令完成
redis-cli --cluster add-node 192.168.1.102:6390 192.168.1.102:6380
redis-cli --cluster add-node 192.168.1.102:6391 192.168.1.102:6380
打印出进群每个节点信息后,reshard命令需要确认迁移的槽数量,这里我们输入4096个:
How many slots do you want to move (from 1 to 16384)? 4096
输入6390的节点ID作为目标节点,也就是要扩容的节点,目标节点只能指定一个
What is the receiving node ID? 6390的ID号
之后输入源节点的ID,这里分别输入每个主节点的6380的ID最后输入done,或者直接输入all
Source node #1:all
迁移完成后命令会自动退出,这时候我们查看一下集群的状态
./redis-trib.rb rebalance 192.168.1.102:6380 #旧版本
或
redis-cli --cluster rebalance 192.168.1.102:6380 #新版本
##############################################################################################
工具收缩节点
流程说明
1).首先需要确定下线节点是否有负责的槽,
如果是,需要把槽迁移到其他节点,保证节点下线后整个集群槽节点映射的完整性.
2).当下线节点不再负责槽或者本身是从节点时,
就可以通知集群内其他节点忘记下线节点,当所有的节点忘记该节点后可以正常关闭.
这里我们准备将刚才新添加的节点下线,也就是6390和6391
收缩和扩容迁移的方向相反,6390变为源节点,其他节点变为目标节点,源节点把自己负责的4096个槽均匀的迁移到其他节点上,.
由于redis-trib..rb reshard命令只能有一个目标节点,因此需要执行3次reshard命令,分别迁移1365,1365,1366个槽.
操作命令:
cd /opt/redis_cluster/redis/src/
./redis-trib.rb reshard 192.168.1.102:6380
How many slots do you want to move (from 1 to 16384)? 1365
输入6380的id
输入6390的id
done
忘记节点
由于我们的集群是做了高可用的,所以当主节点下线的时候从节点也会顶上,所以最好我们先下线从节点,然后在下线主节点
cd /opt/redis_cluster/redis/src/
./redis-trib.rb del-node 192.168.1.102:6391 ID
./redis-trib.rb del-node 192.168.1.102:6390 ID
################################################################################################################
Redis集群常用命令
集群(cluster)
CLUSTER INFO 打印集群的信息
CLUSTER NODES 列出集群当前已知的所有节点(node),以及这些节点的相关信息。
节点(node)
CLUSTER MEET
CLUSTER FORGET
CLUSTER REPLICATE
CLUSTER SAVECONFIG 将节点的配置文件保存到硬盘里面。
槽(slot)
CLUSTER ADDSLOTS
CLUSTER DELSLOTS
CLUSTER FLUSHSLOTS 移除指派给当前节点的所有槽,让当前节点变成一个没有指派任何槽的节点。
CLUSTER SETSLOT
CLUSTER SETSLOT
CLUSTER SETSLOT
CLUSTER SETSLOT
键 (key)
CLUSTER KEYSLOT
CLUSTER COUNTKEYSINSLOT
Redis运维工具
1.运维脚本
[root@redis1 ~]# 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
2.数据导入导出工具
需求背景
刚切换到redis集群的时候肯定会面临数据导入的问题,所以这里推荐使用redis-migrate-tool工具来导入单节点数据到集群里
官方地址:
http://www.oschina.net/p/redis-migrate-tool
安装工具
cd /opt/redis_cluster/
git clone https://github.com/vipshop/redis-migrate-tool.git
cd redis-migrate-tool/
autoreconf -fvi
./configure
make && make install
创建配置文件
[root@redis1 ~]# cat redis_6379_to_6380.conf
[source]
type: single
servers:
- 192.168.1.102:6379
[target]
type: redis cluster
servers:
- 192.168.1.102:6380
[common]
listen: 0.0.0.0:8888
source_safe: true
生成测试数据
[root@redis1 ~]# cat input_key.sh
#!/bin/bash
for i in $(seq 1 1000)
do
redis-cli -c -h redis1 -p 6379 set k_${i} v_${i} && echo "set k_${i} is ok"
done
执行导入命令
[root@redis1 ~]# redis-migrate-tool -c redis_6379_to_6380.conf
数据校验
[root@redis1 ~]# redis-migrate-tool -c redis_6379_to_6380.conf -C redis_check
3.分析键值大小
需求背景
redis的内存使用太大键值太多,不知道哪些键值占用的容量比较大,而且在线分析会影响性能.
安装工具
yum install python-pip gcc python-devel
cd /opt/
git clone https://github.com/sripathikrishnan/redis-rdb-tools
cd redis-rdb-tools
python setup.py install
使用方法
cd /data/redis_cluster/redis_6380/
rdb -c memory redis_6380.rdb -f redis_6380.rdb.csv
分析rdb并导出
awk -F ',' '{print $4,$2,$3,$1}' redis_6380.rdb.csv |sort > 6380.txt
4.监控过期键
需求背景
因为开发重复提交,导致电商网站优惠卷过期时间失效
问题分析
如果一个键已经设置了过期时间,这时候在set这个键,过期时间就会取消
解决思路
如何在不影响机器性能的前提下批量获取需要监控键过期时间
1.Keys * 查出来匹配的键名。然后循环读取ttl时间
2.scan * 范围查询键名。然后循环读取ttl时间
Keys 重操作,会影响服务器性能,除非是不提供服务的从节点
Scan 负担小,但是需要去多次才能取完,需要写脚本
脚本内容:
cat 01get_key.sh
#!/bin/bash
key_num=0
> key_name.log
for line in $(cat key_list.txt)
do
while true
do
scan_num=$(redis-cli -h 192.168.47.75 -p 6380 SCAN ${key_num} match ${line}\* count 1000|awk 'NR==1{print $0}')
key_name=$(redis-cli -h 192.168.47.75 -p 6380 SCAN ${key_num} match ${line}\* count 1000|awk 'NR>1{print $0}')
echo ${key_name}|xargs -n 1 >> key_name.log
((key_num=scan_num))
if [ ${key_num} == 0 ]
then
break
fi
done
done