Redis 集群

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Redis 集群相关的知识,希望对你有一定的参考价值。

1 集群介绍

Redis Cluster 是 redis的分布式解决方案,在3.0版本正式推出 
当遇到单机、内存、并发、流量等瓶颈时,可以采用Cluster架构方案达到负载均 
衡目的。 
Redis Cluster之前的分布式方案有两种:

    1. 客户端分区方案,优点分区逻辑可控,缺点是需要自己处理数据路由,高可用和 
      故障转移等。

    2. 代理方案,优点是简化客户端分布式逻辑和升级维护便利,缺点加重架构部署夫 
      再度和性能消耗。

官方提供的 Redis Cluster集群方案,很好的解决了集群方面的问题

1.1集群数据分布

分布式数据库首先要解决把整个数据库集按照分区规则映射到多个节点的问题,即把数据集划分到多个节点上,每个节点负责整体数据的一个子集,需要关注的是数据分片规则, Redis Cluster采用哈希分片规则。

 

2 Redis 集群部署规划

 

IP端口 角色 软件版本 主机名
10.0.0.130:6380 主节点 redis-3.2.9 redis-130
10.0.0.130:6381 复制节点<=> redis-3.2.9 redis-130
10.0.0.131:6380 主节点 redis-3.2.9 redis-131
10.0.0.131:6381 复制节点 redis-3.2.9 redis-131
10.0.0.132:6380 主节点 redis-3.2.9 redis-132
10.0.0.132:6381 复制节点 redis-3.2.9 redis-132

拓扑图:

不同机器互为主从关系 ,而不是同一台机器为主从关系

每个机器都关闭了selinux,防火墙

 

3 Redis 集群部署

Redis 集群部署分为两种部署: 
1):手动部署 
2):ruby 方式部署Redis 集群

3.1 手动部署 Redis 集群

3.1.1 目录规划

所有机器操作

#配置文件的目录
mkdir -p /opt/redis_cluster/redis_{6380,6381}/{conf,logs,pid}
tree /opt/redis_cluster/redis_638*
/opt/redis_cluster/redis_6380
├── conf
├── logs
└── pid
/opt/redis_cluster/redis_6381
├── conf
├── logs
└── pid

#数据存放的目录
mkdir -p /data/redis_cluster/redis_{6380,6381}

  

所有机器 安装Redis

操作命令集合

### 创建下载目录,安装目录和数据目录
mkdir -p /data/soft
mkdir -p /data/redis_cluster/redis_6379
mkdir -p /opt/redis_cluster/redis_6379/{conf,pid,logs}
### 下载软件和创建软链接
cd /data/soft/
wget http://download.redis.io/releases/redis-3.2.9.tar.gz
tar zxf redis-3.2.9.tar.gz -C /opt/redis_cluster/
ln -s /opt/redis_cluster/redis-3.2.9/ /opt/redis_cluster/redis
#软连接作用方便升级 

###编译安装
cd /opt/redis_cluster/redis
make && make install

生成配置文件和启动脚本比如redis-cli命令
cd /opt/redis_cluster/redis/utils/
./install_server.sh
/opt/redis_cluster/redis_6379/conf/redis_6379.conf

/opt/redis_cluster/redis_6379/logs/redis_6379.log
/data/redis_cluster/redis_6379

  

 

Redis 集群主节点的配置文件 

10.0.0.130:6380的配置文件

[root@redis-130 conf]# cat /opt/redis_cluster/redis_6380/conf/redis_6380.conf 
bind  10.0.0.130
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

  

redis 的集群的配置文件就多加了3个参数,其他的跟redis的配置文件一样 

cluster-enabled yes 启用集群模式 
cluster-config-file nodes_6380.conf 集群的配置文件,单独维护集群配置文件,自动生成 
cluster-node-timeout 15000 集群的超时时间

10.0.0.130:6381 的配置文件

[root@redis-130 conf]# cat /opt/redis_cluster/redis_6381/conf/redis_6381.conf 
bind  10.0.0.130
port 6381
daemonize yes
pidfile "/opt/redis_cluster/redis_6381/pid/redis_6381.pid"
logfile "/opt/redis_cluster/redis_6381/logs/redis_6381.log"
dbfilename "redis_6381.rdb"
dir "/data/redis_cluster/redis_6381/"
cluster-enabled yes
cluster-config-file nodes_6381.conf
cluster-node-timeout 15000

  

其他的两个节点的配置文件用配置文件,利用scp拷贝,然后修改其他节点的修改配置文件的ip地址

[root@redis-130 conf]# scp -r /opt/redis_cluster/redis_638* 10.0.0.131:/opt/redis_cluster/
[root@redis-130 conf]# scp -r /opt/redis_cluster/redis_638* 10.0.0.132:/opt/redis_cluster/

# 修改131 节点配置文件的的ip地址
[root@redis-131 ~]# find /opt/redis_cluster/redis_638* -type f -name "*.conf"|xargs sed -i \'s#130#131#g\'

#修改132 节点配置文件的的ip地址
[root@redis-132 ~]# find /opt/redis_cluster/redis_638* -type f -name "*.conf"|xargs sed -i \'s#130#132#g\'

  

131的机器的配置文件如下

 

 所有节点创建数据目录

[root@redis-130 ~]# mkdir /data/redis_cluster/redis_{6380,6381}
[root@redis-131 ~]# mkdir /data/redis_cluster/redis_{6380,6381}
[root@redis-132 ~]# mkdir /data/redis_cluster/redis_{6380,6381}

  

132的机器配置文件如下

 

 

3.1.2 Redis 集群节点发现

redis管理脚本

 

[root@redis-130 scripts]# cat /root/scripts/redis_shell.sh 
#!/bin/bash
#Author:keme
#Blog: https://www.cnblogs.com/keme/
#Time:2018-11-15 
#Name:redis_start.sh
#Version:V1.0
#Description:This is a test script.

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@redis-130 scripts]# scp /root/scripts/redis_shell.sh 10.0.0.131:/root/scripts/  
[root@redis-130 scripts]# scp /root/scripts/redis_shell.sh 10.0.0.132:/root/scripts/

  

简单介绍用法

[root@redis-130 scripts]# sh redis_shell.sh 
sh redis_shell.sh {start|stop|restart|login|ps|tail} PORT
例如:
sh redis_shell.sh start 6380 表示启动6380 这个服务

start : 开启服务
stop  : 停止服务
restart: 重启服务
login : 登录redis
ps :  查看redis 服务状态
tail: 查看redis 日志

  

 启动所有节点

130机器
[root@redis-130 scripts]# sh redis_shell.sh start 6380
[root@redis-130 scripts]# sh redis_shell.sh start 6381

131机器
[root@redis-131 scripts]# sh redis_shell.sh start 6380
[root@redis-131 scripts]# sh redis_shell.sh start 6381

132机器
[root@redis-132 scripts]# sh redis_shell.sh start 6380
[root@redis-132 scripts]# sh redis_shell.sh start 6381

  

所有机器启动玩服务状态如下

当把所有节点都启动后查看进程会有cluster的字样

查看数据目录的集群配置

[root@redis-130 scripts]#  tree /data/redis_cluster/redis_638*
/data/redis_cluster/redis_6380
└── nodes_6380.conf
/data/redis_cluster/redis_6381
└── nodes_6381.conf

  

[root@redis-130 scripts]# cat /data/redis_cluster/redis_6380/nodes_6380.conf 
640ff147f759bbef50b2b3228f7a79af6afdc5bd :0 myself,master - 0 0 0 connected
vars currentEpoch 0 lastVoteEpoch 0
[root@redis-130 scripts]# cat /data/redis_cluster/redis_6381/nodes_6381.conf 
3ab344e394a65bb9b418c7a719fe9e5f67b15f59 :0 myself,master - 0 0 0 connected
vars currentEpoch 0 lastVoteEpoch 0

  

登录redis,查看集群状态和节点信息

  

 

 

 是因为集群的节点之间还没有互相发现, 所以每个节点都只能看到自己

查看后发现只有自己的节点内容,等节点全部发现后会把所发现的节点ID写入这个文件nodes_6380.conf

 

集群是怎么感知和维护:

集群模式的Redis除了原有的配置文件之外又加了一份集群配置文件.当集群内节点 
信息发生变化,如添加节点,节点下线,故障转移等.节点会自动保存集群状态到配置文件. 
需要注意的是,Redis自动维护集群配置文件,不需要手动修改,防止节点重启时产生错乱.

 

 

 

 配置集群节点发现,只需要在一台机器上操作就可以了

10.0.0.130:6380> CLUSTER MEET 10.0.0.130 6381
OK
10.0.0.130:6380> CLUSTER MEET 10.0.0.131 6380 
OK
10.0.0.130:6380> CLUSTER MEET 10.0.0.131 6381 
OK
10.0.0.130:6380> CLUSTER MEET 10.0.0.132 6380
OK
10.0.0.130:6380> CLUSTER MEET 10.0.0.132 6381
OK
10.0.0.130:6380> CLUSTER NODES

  

 

查看集群配置的配置文件

0.130

 

0.131

 

 0.132

 

 

 小结: 
集群的发现只需要在一台机器上操作即可 
集群之间会互相发现并把节点信息写入到集群配置文件中去

 

3.1.3 集群通信流程

在分布式存储中需要提供维护节点元数据信息的机制, 所谓元数据是指:节点负责哪些数据, 是否出现故障灯状态信息,redis 集群采用 Gossip(流言)协议, Gossip 协议工作原理就是节点彼此不断交换信息,一段时间后所有的节点都会知道集群完整信息, 这种方式类似流言传播。 

通信过程:

  1. 集群中的每一个节点都会单独开辟一个 Tcp 通道,用于节点之间彼此通信,通信端口在基础端口上加10000

 

 如果开启防火墙,加规则的时候也要把这几个端口给开放

    1. 每个节点在固定周期内通过特定规则选择结构节点发送 ping 消息

    2. 接收到 ping 消息的节点用 pong 消息作为响应。集群中每个节点通过一定规则挑选要通信的节点, 每个节点可能知道全部节点, 也可能仅知道部分节点,只要这些节点彼此可以正常通信, 最终他们会打成一致的状态, 当节点出现故障, 新节点加入, 主从角色变化等, 它能够给不断的 ping/pong消息, 从而达到同步目的。 

Redis Cluster Gossip 消息
Gossip 协议职责就是信息交换, 信息交换的载体就是节点间彼此发送Gossip 消息。 常见 Gossip 消息分为: ping、 pong、 meet、 fail 等

meet 消息: 用于通知新节点加入, 消息发送者通知接受者加入到当前集群, meet 消息通信正常完成后, 接收节点会加入到集群中并进行舟曲性的 ping、 pong 消息交换

ping 消息:集群内交换最频繁的消息, 集群内每个节点每秒想多个其他节点发送 ping 消息,用于检测节点是否在线和交换彼此信息。

Pong 消息:当接收到 ping, meet 消息时,作为相应消息回复给发送方确认消息正常通信, 节点也可以向集群内广播自身的 pong 消息来通知整个集群对自身状态进行更新。

fail 消息:当节点判定集群内另一个节点下线时, 回向集群内广播一个fail 消息, 其他节点收到 fail 消息之后把对应节点更新为下线状态。

 

3.1.4 分配槽位

虽然集群的节点已经都互相发现了,但是此时查看集群状态会发现还是fail

这是因为我们还没有分配任何槽位给redis集群,并且必须是所有槽位全部分配完才会认为集群是正常的.有一个槽位没有分配都不可以.

 槽位分配分为手动和自动 

 所以手动分配槽位有可能会漏掉槽位分配。

手动分配 
我们虽然有6个节点,但是真正负责数据写入的只有3个节点,其他3个节点只是作为主节点的从节点,也就是说,只需要分配期中三个节点的槽位就可以了 

分配槽位的方法: 
分配槽位需要在每个主节点上来配置,此时有两种方法执行: 
1.分别登录到每个主节点的客户端来执行命令 
2.在其中一台机器上用redis客户端远程登录到其他机器的主节点上执行命令 

哈希槽 的概念 

https://www.cnblogs.com/zhuifeng-mayi/p/9306998.html 

槽位一共有16384 个 ,平均分配给3个节点也就是0-16383 平均分配给3个机器

 

 分配槽位的操作 
需要在每台机器上的主节点分别执行命令

[root@redis-130 scripts]# redis-cli -h 10.0.0.130 -p 6380 cluster addslots {0..5460}
OK

[root@redis-131 scripts]# redis-cli -h 10.0.0.131 -p 6380 cluster addslots {5461..10922}
OK

[root@redis-132 ~]# redis-cli -h 10.0.0.132 -p 6380 cluster addslots {10923..16383}
OK

  

 分配了槽位以后查看集群状态:

 

 这时候集群就是可用的状态

3.1.5 创建集群的复制关系

虽然这时候集群是可用的了,但是整个集群只要有一台机器坏掉了,那么整个集群都是不可用的. 

所以这时候需要用到其他三个节点分别作为现在三个主节点的从节点,以应对集群主节点故障时可以进行自动切换以保证集群持续可用.

这里需要注意的是,不要让复制节点复制本机器的主节点, 因为如果那样的话机器挂了集群还是不可用状态, 所以复制节点要复制其他服务器的主节点. 
然而使用ruby工具自动分配的时候有概率会出现复制节点和主节点在同一台机器上的情况,需要注意这里的复制拓扑如下:

 

 

 因为集群之间互通的,所以在一台机器上操作就可以

# 130:6381从 -> 132:6380主   : replicate 后面跟的是132主节点配置文件的hash值 
[root@redis-130 scripts]# redis-cli -h 10.0.0.130 -p 6381 CLUSTER REPLICATE 51be127b6c6c2c6fff184c61b2d7276e9559fa1b
OK

# 131:6381从 -> 130:6380主 :replicate 后面跟的是130主节点配置文件的hash值 

[root@redis-130 scripts]# redis-cli -h 10.0.0.131 -p 6381 CLUSTER REPLICATE 640ff147f759bbef50b2b3228f7a79af6afdc5bd
OK


# 132:6381从 -> 131:6380主 :replicate 后面跟的是131主节点配置文件的hash值 

[root@redis-130 scripts]# redis-cli -h 10.0.0.132 -p 6381 CLUSTER REPLICATE 036036c10b33231def1e0a7b12edc90b7f3eb12a
OK

  

 

注: 
1.需要执行命令的是每个服务器的从节点 
2.注意主从的ID不要搞混了

3.1.6 测试Redis Cluster

使用常规插入redis数据的方式往集群里写入数据看看会发生什么

### 测试写入一条数据
[root@redis-130 scripts]# redis-cli -h 10.0.0.130 -p 6380 set key_1 value_1
(error) MOVED 11998 10.0.0.132:6380

  

结果提示error, 但是给出了集群另一个节点的地址那么这条数据到底有没有写入呢? 我们登录这两个节点分别查看

# 登录 130:6380 由于槽的概念 ,该key的数据分配的到9189的槽  
[root@redis-130 scripts]# sh redis_shell.sh login 6380
10.0.0.130:6380> get key1
-> Redirected to slot [9189] located at 10.0.0.131:6380
(nil)
10.0.0.131:6380> get key1
(nil)
10.0.0.131:6380> 


# 登录 132:6380
[root@redis-132 scripts]# sh redis_shell.sh login 6380
10.0.0.132:6380> get key_1
(nil)

  

结果都没有,这是因为使用集群后由于数据被分片了,所以并不是说在那台机器上写入 
数据就会在哪台机器的节点上写入,集群的数据写入和读取就涉及到了另外一个概念,ASK路由

 

Redis Cluster ASK 路由介绍 
在集群模式下,Redis接受任何键相关命令时首先会计算键对应的槽,再根据槽找出所对应的节点,如果节点是自身,则处理键命令;否则回复MOVED重定向错误,通知客户端请求正确的节点,这个过程称为Mover重定向.

知道了ask路由后,我们使用-c选项批量插入一些数据

[root@redis-130 scripts]# vim redis_input.sh
redis_input.sh  redis_shell.sh
[root@redis-130 scripts]# chmod +x redis_input.sh 
[root@redis-130 scripts]# cat redis_input.sh 
#!/bin/bash
for i in $(seq 1 1000)
do
redis-cli -c -h 10.0.0.130 -p 6380 set key_${i} value_${i} && echo "set key_${i} is ok"
done

[root@redis-130 scripts]# ./redis_input.sh 

  都会写入到正确的节点 
看一下每个集群节点的数据分配量

10.0.0.130:6380> dbsize
(integer) 334

10.0.0.131:6380> dbsize
(integer) 336

10.0.0.132:6380> dbsize
(integer) 330

  

集群就搭建完毕了

3.2 ruby 部署 redis 集群

因为上面全是手动,分配槽位有可能会出问题,检查集群的状态等等有点不方便。

推荐使用ruby 方式安装

3.2.1 安装ruby 环境
[root@redis-130 scripts]# yum -y install rubygems #安装ruby
[root@redis-130 scripts]# ruby --version   # 查看ruby 版本
ruby 2.0.0p648 (2015-12-16) [x86_64-linux]

[root@redis-130 scripts]# gem sources -a  http://mirrors.aliyun.com/rubygems/  #添加国内的ruby 源
http://mirrors.aliyun.com/rubygems/ added to sources

[root@redis-130 scripts]#  gem sources --remove https://rubygems.org/
https://rubygems.org/ removed from sources  # 移除原来的源
[root@redis-130 scripts]# gem install redis -v 3.3.5 #redis和ruby的接口,使用gem 安装 所以才要安装的

  

3.2.2 目录规划

为了区分, 在分配2个目录分别为6390和6391,然后传送给其他的另外两个节点

[root@redis-130 ~]# mkdir -p /opt/redis_cluster/redis_{6390,6391}/{conf,logs,pid}
[root@redis-130 ~]# cd /opt/redis_cluster/
[root@redis-130 redis_cluster]# cp redis_6380/conf/redis_6380.conf redis_6390/conf/redis_6390.conf
[root@redis-130 redis_cluster]#  cp redis_6381/conf/redis_6381.conf redis_6391/conf/redis_6391.conf
[root@redis-130 redis_cluster]# find redis_639* -type f -name "*conf"|xargs sed -i "s#638#639#g"
[root@redis-130 redis_cluster]# mkdir /data/redis_cluster/redis_{6390,6391}

#传送给其他节点
[root@redis-130 redis_cluster]# scp -r redis_639* 10.0.0.131:/opt/redis_cluster/
[root@redis-130 redis_cluster]# scp -r redis_639* 10.0.0.132:/opt/redis_cluster/
[root@redis-130 redis_cluster]# scp -r /data/redis_cluster/redis_639* 10.0.0.131:/data/redis_cluster/
[root@redis-130 redis_cluster]# scp -r /data/redis_cluster/redis_639* 10.0.0.132:/data/redis_cluster/

  

其他节点修改配置文件里的IP地址

[root@redis-131 scripts]# find /opt/redis_cluster/redis_639* -type f -name "*conf"|xargs sed -i "s#0.130#0.131#g"
[root@redis-132 scripts]# find /opt/redis_cluster/redis_639* -type f -name "*conf"|xargs sed -i "s#0.130#0.132#g"

  

最后一定注意看看配置文件是否配置正确 

130机器

 

 131机器

 

 

 132机器

 

3.2.3 开始部署

首先所有服务器上启动节点

redis-server /opt/redis_cluster/redis_6390/conf/redis_6390.conf
redis-server /opt/redis_cluster/redis_6391/conf/redis_6391.conf

  

 

 

 

进入redis的安装目录下的src目录

[root@redis-130 src]# cd /opt/redis_cluster/redis/src/

  

执行创建redis 集群命令

[root@redis-130 src]# ./redis-trib.rb create --replicas 1  10.0.0.130:6390 10.0.0.131:6390 10.0.0.132:6390 10.0.0.130:6391 10.0.0.131:6391 10.0.0.132:6391

  

 

检查集群完整性

[root@redis-130 src]# ./redis-trib.rb check 10.0.0.130:6390

  

OK ,集群搭建成功

3.3 模拟集群故障

集群正常的节点信息:

集群结构

 

停掉期中一台主机的redis节点,然后查看一下集群的变化

使用暴力的kill -9杀掉 10.0.0.132:6380上的,redis集群节点,然后观察节点状态理想情况应该是130上的6381从节点升级为主节点

 

 

是的130 的6381 升级为主节点了

 

是否所有键都已转移过去

yes 已转移过去

3.3.1 故障修复上线

已经测试了故障切换的功能,但是节点修复后还是需要重新上线所以这里测试节点重新上线后的操作

重新启动132的6380,然后观察日志

 

 观察130 的6381 日志

10.0.0.132:6380启动起来是变成了10.0.0.130:6381的从

假如想让修复后的节点重新上线,可以执行 
在修复后的那个节点执行CLUSTER FAILOVER命令

[root@redis-132 scripts]# ./redis_shell.sh login 6380
10.0.0.132:6380> CLUSTER FAILOVER
OK

  

看些 10.0.0.132: 6380 的日志

 

 看看10.0.0.130 的6381 的日志

 

看看节点信息

 

ok,恢复成功

4 redis 集群扩充节点

4.1 手动扩容节点

4.1.1 扩容规划

Redis集群的扩容操作可分为以下几个步骤 
1)准备新节点 
2)加入集群 
3)迁移槽和数据

4.1.2 新节点安装配置

注意,因为集群高可用是主从来保证的,所以新节点也至少需要2个,这里我们定为6395和6396 
创建目录和复制修改配置文件

[root@redis-130 ~]# mkdir -p /opt/redis_cluster/redis_{6395,6396}/{conf,logs,pid}
[root@redis-130 ~]# mkdir -p /data/redis_cluster/redis_{6395,6396}
[root@redis-130 ~]# cd /opt/redis_cluster/
[root@redis-130 redis_cluster]# cp redis_6380/conf/redis_6380.conf redis_6395/conf/redis_6395.conf
[root@redis-130 redis_cluster]# cp redis_6380/conf/redis_6380.conf redis_6396/conf/redis_6396.conf
[root@redis-130 redis_cluster]# sed -i \'s#6380#6395#g\' redis_6395/conf/redis_6395.conf
[root@redis-130 redis_cluster]# sed -i \'s#6380#6396#g\' redis_6396/conf/redis_6396.conf

  

启动新节点

[root@redis-130 redis_cluster]# cd /root/scripts/
[root@redis-130 scripts]# sh redis_shell.sh start 6395
[root@redis-130 scripts]# sh redis_shell.sh start 6396

  

 

 启动完成后也需要加入集群,在已经是集群里任意一个节点登录然后执行cluster meet

[root@redis-130 scripts]# sh redis_shell.sh login 6380
10.0.0.130:6380> CLUSTER MEET 10.0.0.130 6395
OK
10.0.0.130:6380> CLUSTER MEET 10.0.0.130 6396
OK

  

 

可以看到这时新添加的节点都是master状态,但是由于没有分配槽位,所以不能接受任何读写操作,对于新添加的节点一般有两种选择:

1.为它迁移槽位和数据实现扩容 
2.作为其他主节点的从节点负责故障转移

 注意: 
生产环境建议使用ruby脚本来实现收缩扩容,因为会有一些安全检查机制

4.1.3 制造一些数据

为了看效果,这里我首先制造一些数据

[root@redis-130 scripts]# cat /root/scripts/redis_input.sh 
#!/bin/bash
for i in $(seq 1 10000)
do
redis-cli -c -h 10.0.0.130 -p 6380 set key_${i} value_${i} && echo "set key_${i} is ok"
done

[root@redis-130 scripts]# /root/scripts/redis_input.sh # 执行生成测试数据

  

4.1.4 分配槽位给新节点

槽是Redis集群管理数据的基本单位。 
流程说明:

    1. 槽迁移计划 
      首先要确定原有节点的哪些槽需要迁移到新节点迁移计划要确保每个节点负责相似数量的槽.从而保证各节点的数据均匀,

    2. 迁移数据 
      数据迁移过程是逐个槽进行的,流程说明如下 
      1)对目标节点发送cluster setslot {slot} importing {sourceNodeId}命令,让目标节点准备导入槽的数据 
      2)对源节点发送cluster setslot {slot} importing {targetNodeId}命令,让源节点准备迁出槽的数据 
      3)源节点循环执行cluster getkeysinslot {slot} {count}命令.获取count个属于槽{slot}的键. 
      4)在源节点上执行migrate {targetIp} {targetPort} “” 0 {timeout} keys {keys …}命令.把获取的键通过流水线(pipeline)机制批量迁移到目标节点,这个命令在Redis 3.0.6以上版本提供,之前的版本只能单键迁移. 
      5)重复步骤3和步骤4,直到槽下所有的键值数据都迁移到目标节点 
      6)向集群内所有主节点发送cluster setslot {slot} node {targetNodeId}命令,通知槽分配给目标节点,为了保证槽节点映射变化及时传播, 
      需要遍历发送给所有主节点更新被迁移的槽指向新节点,

想想3个节点的数据 ,一共是16384个槽平均每个主节点是5461个槽,4个节点平均每个主节点是4096 个槽位,

下面是操作步骤: 
查看现有通过cluster nodes 获得节点负责的槽位,然后查看其中一个槽4096槽的数据

10.0.0.130:6380> CLUSTER GETKEYSINSLOT 4096 100
1) "key_1001"
2) "key_4773"

  

1)目标节点准备导入槽4096数据,也就是新节点的主节点6395

# 设置 6395 主节点 导入槽4096 数据
10.0.0.130:6395> CLUSTER SETSLOT 4096 importing 036036c10b33231def1e0a7b12edc90b7f3eb12a 
OK

  

确认槽4096导入状态开启

 

 

2)源节点(0.130:6380)准备导出槽4096数据

[root@redis-130 scripts]# sh redis_shell.sh login 6380
10.0.0.130:6380> CLUSTER SETSLOT 4096 migrating 65b101d330ceff430607e4f8712ff8cfe11006ec
OK

  

确认槽位4096导出状态开启

3)批量获取槽4096对应的键

10.0.0.130:6380> CLUSTER GETKEYSINSLOT 4096 100
1) "key_1001"
2) "key_4773"

  

 确认这几个键存在于源节点

10.0.0.130:6380> mget key_1001 key_4773
1) "value_1001"
2) "value_4773"

  

批量迁移这几个键,migrate命令保证了每个键迁移过程的原子性:

10.0.0.130:6380> MIGRATE 10.0.0.130 6395 "" 0 5000 keys key_1001 key_4773
OK

  

这里再次查询一下这几个键,发现返回了ASK转向错误,ASK负责引导客户端找到数据所在的节点

10.0.0.130:6380> mget key_1001 key_4773
(error) ASK 4096 10.0.0.130:6395

  

4)通知所有主节点4096槽指派给了目标节点6395

[root@redis-130 scripts]# redis-cli -c -h 10.0.0.130 -p 6380 CLUSTER SETSLOT 4096 node 65b101d330ceff430607e4f8712ff8cfe11006ec 
OK

[root@redis-130 scripts]# redis-cli -c -h 10.0.0.131 -p 6380 CLUSTER SETSLOT 4096 node 65b101d330ceff430607e4f8712ff8cfe11006ec
OK
[root@redis-130 scripts]# redis-cli -c -h 10.0.0.132 -p 6380 CLUSTER SETSLOT 4096 node 65b101d330ceff430607e4f8712ff8cfe11006ec
OK
[root@redis-130 scripts]# redis-cli -c -h 10.0.0.130 -p 6395 CLUSTER SETSLOT 4096 node 65b101d330ceff430607e4f8712ff8cfe11006ec
OK

  

4.1.5 检查集群状态

 

 查看新节点的4096槽

[root@redis-130 scripts]# sh redis_shell.sh login 6395 
10.0.0.130:6395> CLUSTER GETKEYSINSLOT 4096 100
1) "key_1001"
2) "key_4773"
10.0.0.130:6395> keys *
1) "key_4773"
2) "key_1001"

  

 

ok

注意: 
这里只是迁移了一个槽的的数据迁移到新节点

4.2 ruby 脚本扩充节点

手动扩充的方式,太麻烦,不智能。

4.2.1 redis-trib 命令说明
redis-trib.rb reshard host:port --from <arg> --to <arg> --slots <arg> --yes --timeout <age> --pipeline <arg>

  

参数说明:

host:port #必传参数,集群内任意节点地址,用来获取整个集群信息
--from   #指定源节点的id,如果有多个源节点,用逗号分隔
--to     #需要迁移的目标节点的id,目标节点只能填写一个,在迁移过程提示用户输入
--slots   #需要迁移槽的数量,在迁移过程提示用户输入
--yes    #当打印出reshard执行计划时,是否需要用户输入yes再执行 
--timeout  #控制每次migrate操作的超时时间,默认为60 000毫秒
--pipeline #控制每次批量迁移键的数量,默认为10

  

4.2.2 迁移数据
[root@redis-130 scripts]# cd /opt/redis_cluster/redis/src/
[root@redis-130 src]# ./redis-trib.rb reshard 10.0.0.130:6380

  

 

打印出进群每个节点信息后,reshard命令需要确认迁移的槽数量,这里我们输入4096个:

How many slots do you want to move (from 1 to 16384)? 4096

  

输入6395的节点ID作为目标节点,也就是要扩容的节点,目标节点只能指定一个

What is the receiving node ID? 65b101d330ceff430607e4f8712ff8cfe11006ec

  

之后输入源节点的ID,这里分别输入每个主节点的6380的ID,最后用done表示结束:

Source node #1:640ff147f759bbef50b2b3228f7a79af6afdc5bd
Source node #2:036036c10b33231def1e0a7b12edc90b7f3eb12a
Source node #3:51be127b6c6c2c6fff184c61b2d7276e9559fa1b
Source node #4:done

  

数据迁移前会打印出所有的槽从源节点到目标节点的计划,确认计划无误后输入yes执行迁移工作

 

 

 迁移完成后命令会自动退出,这时候我们查看一下集群的状态
4.2.3 检查集群均衡性
[root@redis-130 src]# ./redis-trib.rb rebalance 10.0.0.130:6380
>>> Performing Cluster Check (using node 10.0.0.130:6380)
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
*** No rebalancing needed! All nodes are within the 2.0% threshold.

  

 

4.2.4 添加新节点的从节点

由于新加的节点 是两个节点, 10.0.0.130:6395 和10.0.0.131:6396 ,6395和6396都是加入了集群节点, 没有配置主从关系,默认加入集群都是主节点,由于10.0.0.130:6395 已近加入了集群, 分配了槽位。这时候应该把 6396变成6395的从节点。

 [root@redis-130 src]# redis-cli -c -h 10.0.0.130 -p 6396 CLUSTER REPLICATE 65b101d330ceff430607e4f8712ff8cfe11006ec
OK

  

 

 

5 redis 收缩节点

5.1 redis-trib 收缩节点

流程说明 : 
1).首先需要确定下线节点是否有负责的槽,如果是,需要把槽迁移到其他节点,保证节点下线后整个集群槽节点映射的完整性. 
2).当下线节点不再负责槽或者本身是从节点时,就可以通知集群内其他节点忘记下线节点,当所有的节点忘记该节点后可以正常关闭.

5.1.1 下线迁移槽过程

这里准备将刚才新添加的节点下线,也就是6395和6396 
收缩和扩容迁移的方向相反,6395变为源节点,其他节点变为目标节点,源节点把自己负责的4096个槽均匀的迁移到其他节点上。 
由于redis-trib..rb reshard命令只能有一个目标节点,因此需要执行3次reshard命令,分别前已1365,1365,1366个槽.

1)输入第一个主节点的地址 10.0.0.130:6380

[root@redis-90 src]# ./redis-trib.rb reshard 10.0.0.130:6380
======================================
 [OK] All 16384 slots covered.

  

输入需要迁移的槽数量

How many slots do you want to move (from 1 to 16384)? 1365

  

输入目标节点id,这里输入的是每个主节点的id . 这里输入的是10.0.0.130:6380的id

What is the receiving node ID? 640ff147f759bbef50b2b3228f7a79af6afdc5bd 

  

 

 输入源节点id,也就是需要下线的节点的id,这里输入10.0.0.130:6395

Source node #1:65b101d330ceff430607e4f8712ff8cfe11006ec
Source node #2:done

  

确认迁移

Do you want to proceed with the proposed reshard plan (yes/no)? yes

  

重复操作迁移分配1365个槽给10.0.0.131::6380

[root@redis-130 src]# ./redis-trib.rb reshard 10.0.0.130:6380 
How many slots do you want to move (from 1 to 16384)? 1365
What is the receiving node ID? 036036c10b33231def1e0a7b12edc90b7f3eb12a
Source node #1:65b101d330ceff430607e4f8712ff8cfe11006ec
Source node #2:done
Do you want to proceed with the proposed reshard plan (yes/no)? yes

  

重复操作迁移分配1366个槽给10.0.0.132:6380

[root@redis-130 src]# ./redis-trib.rb reshard 10.0.0.130:6380 
How many slots do you want to move (from 1 to 16384)? 1366
What is the receiving node ID? 51be127b6c6c2c6fff184c61b2d7276e9559fa1b
Source node #1:65b101d330ceff430607e4f8712ff8cfe11006ec
Source node #2:done
Do you want to proceed with the proposed reshard plan (yes/no)? yes

查看集群状态,可以看到6395已经没有负责的槽位了,所以可以6395和6396就可以安全的下线 

[root@redis-130 scripts]# sh redis_shell.sh login 6395
10.0.0.130:6395> keys *
(empty list or set)

  

没数据了

 

5.1.2 忘记节点

由于我们的集群是做了高可用的,所以当主节点下线的时候从节点也会顶上,所以最好我们先下线从节点10.0.0.130:6396,然后在下线主节点10.0.0.130:6395

 

查看集群的节点:

 

 删除10.0.0.130:6396从节点

[root@redis-130 src]# ./redis-trib.rb del-node 10.0.0.130:6396 ac7b3569f0b146a2fccf13f51dd01115dd8b2b90
>>> Removing node ac7b3569f0b146a2fccf13f51dd01115dd8b2b90 from cluster 10.0.0.130:6396
>>> Sending CLUSTER FORGET messages to the cluster...
>>> SHUTDOWN the node.

  

删除10.0.0.130:6395 主节点

[root@redis-130 src]# ./redis-trib.rb del-node 10.0.0.130:6395 65b101d330ceff430607e4f8712ff8cfe11006ec
>>> Removing node 65b101d330ceff430607e4f8712ff8cfe11006ec from cluster 10.0.0.130:6395
[ERR] Node 10.0.0.130:6395 is not empty! Reshard data away and try again.

  

删除失败了 
这时虽然没有槽位了,但是使用redis-trid却删除不了,提示数据非空, 

解决: 
首先确认节点确实没有真正的槽位了.然后手动删掉这个槽位,

集群显示正常,也没有什么导入状态

检查集群的状态,还是有一个槽位

这个10.0.0.130:6395 还有一个槽位 ,在10.0.0.130:6395 删除这个槽位

[root@redis-130 scripts]# sh redis_shell.sh login 6395
10.0.0.130:6395> CLUSTER DELSLOTS 12287
OK

  

这时候下线主节点10.0.0.130:6395

[root@redis-130 src]# ./redis-trib.rb del-node 10.0.0.130:6395 65b101d330ceff430607e4f8712ff8cfe11006ec
>>> Removing node 65b101d330ceff430607e4f8712ff8cfe11006ec from cluster 10.0.0.130:6395
>>> Sending CLUSTER FORGET messages to the cluster...
>>> SHUTDOWN the node.

  

 

 下线节点,redis 的服务也会停止如上图

刚刚不是删除了12287的槽位,然后其他节点在添加这个槽位,我就在10.0.0.130:6380 添加这个槽位

10.0.0.130:6380> CLUSTER ADDSLOTS 12287
OK

  

最后查看集群数据

10.0.0.130:6380> get key_1245
-> Redirected to slot [12832] located at 10.0.0.132:6380
"value_1245"

  

OK ,完整 的集群上线 ,和下线就是这样。

以上是关于Redis 集群的主要内容,如果未能解决你的问题,请参考以下文章

java怎么从多台redis集群取数据库

我在工作中遇到的redis集群使用

redis集群概念

python连接redis,redis集群

Python3 redis集群连接 (带密码验证)

c#程序怎么调用redis集群