高可用架构篇
Redis 集群的安装(Redis3+CentOS)
Redis 官方集群指南:http://redis.io/topics/cluster-tutorial
Redis 官方集群规范:http://redis.io/topics/cluster-spec
Redis 集群指南(中文翻译,紧供参考):http://redisdoc.com/topic/cluster-tutorial.html
Redis 集群规范(中文翻译,紧供参考): http://redisdoc.com/topic/cluster-spec.html (建议学员们在观看视频前,请先对以上参考文档中的内容有个大概的了解
)
简单概括:
redis通过同一个节点的主从复制,数据分片,redis2.0的时候,主节点出现问题,可能就会出现问题,到了redis3.0,分为多个主节点,每个主节点分不同的数据,每个节点主从复制,从而实现整个集群的高可用和高可扩展。
每个节点又可以理解为一个小的集群,每个节点集群有主从复制,两份相同的数据,所有的节点共同构成了整个rendis集群的所有数据。
每个节点的hash槽都设置了从节点,就实现了高可用
Redis 集群数据共享
Redis 集群使用数据分片(sharding)而非一致性哈希(consistency hashing)来实现: 一个 Redis 集群包含 16384 个哈希槽(hash slot), 数据库中的每个键都属于这 16384 个哈希槽的其中一个, 集群使用公式 CRC16(key) % 16384 来计算键 key 属于哪个槽, 其中 CRC16(key) 语句用于计算键 key 的 CRC16 校验和 。
集群中的每个节点负责处理一部分哈希槽。 举个例子, 一个集群可以有三个哈希槽, 其中:
节点 A 负责处理 0 号至 5500 号哈希槽。
节点 B 负责处理 5501 号至 11000 号哈希槽。
节点 C 负责处理 11001 号至 16384 号哈希槽。
这种将哈希槽分布到不同节点的做法使得用户可以很容易地向集群中添加或者删除节点。 比如说:
如果用户将新节点 D 添加到集群中, 那么集群只需要将节点 A 、B 、 C 中的某些槽移动到节点 D 就可以了。
与此类似, 如果用户要从集群中移除节点 A , 那么集群只需要将节点 A 中的所有哈希槽移动到节点 B 和节点 C , 然后再移除空白(不包含任何哈希槽)的节点 A 就可以了。
因为将一个哈希槽从一个节点移动到另一个节点不会造成节点阻塞, 所以无论是添加新节点还是移除已存在节点, 又或者改变某个节点包含的哈希槽数量, 都不会造成集群下线。
Redis 集群中的主从复制
为了使得集群在一部分节点下线或者无法与集群的大多数(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 集群还是会停止运作。
Redis 集群的一致性保证(guarantee)(简单看,一般不会遇到)
Redis 集群不保证数据的强一致性(strong consistency): 在特定条件下, Redis 集群可能会丢失已经被执行过的写命令。
使用异步复制(asynchronous replication)是 Redis 集群可能会丢失写命令的其中一个原因。 考虑以下这个写命令的例子:
客户端向主节点 B 发送一条写命令。
主节点 B 执行写命令,并向客户端返回命令回复。
主节点 B 将刚刚执行的写命令复制给它的从节点 B1 、 B2 和 B3 。
如你所见, 主节点对命令的复制工作发生在返回命令回复之后, 因为如果每次处理命令请求都需要等待复制操作完成的话, 那么主节点处理命令请求的速度将极大地降低 —— 我们必须在性能和一致性之间做出权衡。
Note
如果真的有必要的话, Redis 集群可能会在将来提供同步地(synchronou)执行写命令的方法。
Redis 集群另外一种可能会丢失命令的情况是, 集群出现网络分裂(network partition), 并且一个客户端与至少包括一个主节点在内的少数(minority)实例被孤立。
举个例子, 假设集群包含 A 、 B 、 C 、 A1 、 B1 、 C1 六个节点, 其中 A 、B 、C 为主节点, 而 A1 、B1 、C1 分别为三个主节点的从节点, 另外还有一个客户端 Z1 。
假设集群中发生网络分裂, 那么集群可能会分裂为两方, 大多数(majority)的一方包含节点 A 、C 、A1 、B1 和 C1 , 而少数(minority)的一方则包含节点 B 和客户端 Z1 。
在网络分裂期间, 主节点 B 仍然会接受 Z1 发送的写命令:
如果网络分裂出现的时间很短, 那么集群会继续正常运行;
但是, 如果网络分裂出现的时间足够长, 使得大多数一方将从节点 B1 设置为新的主节点, 并使用 B1 来代替原来的主节点 B , 那么 Z1 发送给主节点 B 的写命令将丢失。
注意, 在网络分裂出现期间, 客户端 Z1 可以向主节点 B 发送写命令的最大时间是有限制的, 这一时间限制称为节点超时时间(node timeout), 是 Redis 集群的一个重要的配置选项:
对于大多数一方来说, 如果一个主节点未能在节点超时时间所设定的时限内重新联系上集群, 那么集群会将这个主节点视为下线, 并使用从节点来代替这个主节点继续工作。
对于少数一方, 如果一个主节点未能在节点超时时间所设定的时限内重新联系上集群, 那么它将停止处理写命令, 并向客户端报告错误。
强烈建议六个节点,三个主三个从,如果只有三个从,其中某个主节点挂了,这个节点的相关槽位的数据就不可用了。如果没有从节点,redis集群是没有意义的,是不能高可用的。
要让 Redis3.0 集群正常工作至少需要 3 个 Master 节点,要想实现高可用,每个 Master 节点要配备 至少 1 个 Slave 节点。根据以上特点和要求,进行如下的集群实施规划:
使用 6 台服务器(物理机或虚拟机)部署 3 个 Master + 3 个 Slave;
图中的蓝绿一组可以理解为是一个redis集群中的节点,数据一模一样,只不过是一个主一个从,两个组成一个小的集群,保存整个redis集群中的一小部分,来保证高可用,但是每两个主节点的数据是不一样的,通过算法算出来的,虽然数据不一样,但是节点之间能够知道其他节点的状态,即能够知道key在对应的哪个节点上
主机名 | IP | 服务端口 | 集群端口 | 主/从 |
---|---|---|---|---|
默认 6379 | 服务端口数+10000 | |||
edu-redis-01 | 192.168.1.111 | 7111 | 17111 | Master |
edu-redis-02 | 192.168.1.112 | 7112 | 17112 | Master |
edu-redis-03 | 192.168.1.113 | 7113 | 17113 | Master |
edu-redis-04 | 192.168.1.114 | 7114 | 17114 | Slave |
edu-redis-05 | 192.168.1.115 | 7115 | 17115 | Slave |
edu-redis-06 | 192.168.1.116 | 7116 | 17116 | Slave |
注意:在配置文件中是没有集群端口这个配置的,因为默认是服务端口+10000
服务端口的作用是主节点宕机之后的选举、互相知道其他节点的状态等作用。
按规划:防火墙中打开相应的端口
192.168.1.111
-A INPUT -m state --state NEW -m tcp -p tcp --dport 7111 -j ACCEPT
-A INPUT -m state --state NEW -m tcp -p tcp --dport 17111 -j ACCEPT
192.168.1.112
-A INPUT -m state --state NEW -m tcp -p tcp --dport 7112 -j ACCEPT
-A INPUT -m state --state NEW -m tcp -p tcp --dport 17112 -j ACCEPT
192.168.1.113
-A INPUT -m state --state NEW -m tcp -p tcp --dport 7113 -j ACCEPT
-A INPUT -m state --state NEW -m tcp -p tcp --dport 17113 -j ACCEPT
192.168.1.114
-A INPUT -m state --state NEW -m tcp -p tcp --dport 7114 -j ACCEPT
-A INPUT -m state --state NEW -m tcp -p tcp --dport 17114 -j ACCEPT
192.168.1.115
-A INPUT -m state --state NEW -m tcp -p tcp --dport 7115 -j ACCEPT
-A INPUT -m state --state NEW -m tcp -p tcp --dport 17115 -j ACCEPT
192.168.1.116
-A INPUT -m state --state NEW -m tcp -p tcp --dport 7116 -j ACCEPT
-A INPUT -m state --state NEW -m tcp -p tcp --dport 17116 -j ACCEPT
安装目录:/usr/local/redis3 用户:root
编译和安装所需的包:
# yum install gcc tcl
下载(或上传)Redis3 最新稳定版(当前最新版 redis-3.0.3.tar.gz) # cd /usr/local/src
# wget http://download.redis.io/releases/redis-3.0.3.tar.gz
创建安装目录:
# mkdir /usr/local/redis3
解压:
# tar -zxvf redis-3.0.3.tar.gz
# cd redis-3.0.3
安装(使用 PREFIX 指定安装目录,安装到指定目录):
# make PREFIX=/usr/local/redis3 install
安装完成后,可以看到/usr/local/redis3 目录下有一个 bin 目录,bin 目录里就是 redis 的命令脚本:
redis-benchmark redis-check-aof redis-check-dump redis-cli redis-server
创建集群配置目录,并拷贝 redid.conf 配置文件到各节点配置目录:
192.168.1.111
# mkdir -p /usr/local/redis3/cluster/7111
# cp /usr/local/src/redis-3.0.3/redis.conf /usr/local/redis3/cluster/7111/redis-7111.conf
192.168.1.112
# mkdir -p /usr/local/redis3/cluster/7112
# cp /usr/local/src/redis-3.0.3/redis.conf /usr/local/redis3/cluster/7112/redis-7112.conf
192.168.1.113
# mkdir -p /usr/local/redis3/cluster/7113
# cp /usr/local/src/redis-3.0.3/redis.conf /usr/local/redis3/cluster/7113/redis-7113.conf
192.168.1.114
# mkdir -p /usr/local/redis3/cluster/7114
# cp /usr/local/src/redis-3.0.3/redis.conf /usr/local/redis3/cluster/7114/redis-7114.conf
192.168.1.115
# mkdir -p /usr/local/redis3/cluster/7115
# cp /usr/local/src/redis-3.0.3/redis.conf /usr/local/redis3/cluster/7115/redis-7115.conf
192.168.1.116
# mkdir -p /usr/local/redis3/cluster/7116
# cp /usr/local/src/redis-3.0.3/redis.conf /usr/local/redis3/cluster/7116/redis-7116.conf
修改配置文件中的下面选项:
6 个节点的 redis.conf 配置文件内容,注意修改下红色字体部分的内容即可,其他都相同:
配置选项 | 选项值 | 说明 |
---|---|---|
daemonize | yes | 是否作为守护进程运行 |
pidfile | /var/run/redis- 7111.pid | 如以后台进程运行,则需指定一个 pid,默认为/var/run/redis.pid |
** port | 7111 | 监听端口,默认为 6379,注意:集群通讯端口值默认为此端口值+10000,如 17111 ** |
databases | 1 | 可用数据库数,默认值为 16,默认数据库存储在 DB 0号 ID 库中,无特殊需求,建议仅设置一个数据库databases 1 |
cluster-enabled | yes | 打开 redis 集群 |
cluster-config-file | /usr/local/redis3/cluster/ 7111/nodes.conf | 集群配置文件(启动自动生成),不用人为干涉 |
cluster-node-timeout | 15000 | 节点互连超时时间。毫秒 |
cluster-migration-barrier | 1 | 数据迁移的副本临界数,这个参数表示的是,一个主节点在拥有多少个好的从节点的时候就要割让一个从节点出来给另一个没有任何从节点的主节点。 |
cluster-require-full- | yes | 如果某一些 key space 没有被集群中任何节点覆盖,集群将停止接受写入。 |
appendonly | yes | 启用 aof 持久化方式因为 redis 本身同步数据文件是按上面 save 条件来同步的,所以有的数据会在一段时间内只存在于内存中。默认值为 no |
dir | /usr/local/redis3/cluster/ 7111 | 节点数据持久化存放目录(建议配置) |
红色的部分是需要着重修改的地方,绿色是最少配置项,蓝色是留心关注的知识点。
包含了最少选项的集群配置文件示例如下:
port 7000
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
appendonly yes
使用如下命令启动这 6 个 Redis 节点实例:
192.168.1.111
# /usr/local/redis3/bin/redis-server /usr/local/redis3/cluster/7111/redis-7111.conf 192.168.1.112
# /usr/local/redis3/bin/redis-server /usr/local/redis3/cluster/7112/redis-7112.conf 192.168.1.113
# /usr/local/redis3/bin/redis-server /usr/local/redis3/cluster/7113/redis-7113.conf 192.168.1.114
# /usr/local/redis3/bin/redis-server /usr/local/redis3/cluster/7114/redis-7114.conf 192.168.1.115
# /usr/local/redis3/bin/redis-server /usr/local/redis3/cluster/7115/redis-7115.conf 192.168.1.116
# /usr/local/redis3/bin/redis-server /usr/local/redis3/cluster/7116/redis-7116.conf
启动之后用 PS 命令查看实例启动情况:
[root@edu-redis-01 cluster]# ps -ef | grep redis
root 5443 1 0 22:49 ? 00:00:00 /usr/local/redis3/bin/redis-server *:7111 [cluster] [root@edu-redis-02 cluster]# ps -ef | grep redis
root 5421 1 0 22:49 ? 00:00:00 /usr/local/redis3/bin/redis-server *:7112 [cluster] [root@edu-redis-03 cluster]# ps -ef | grep redis
root 5457 1 0 22:49 ? 00:00:00 /usr/local/redis3/bin/redis-server *:7113 [cluster] [root@edu-redis-04 cluster]# ps -ef | grep redis
root 5379 1 0 22:50 ? 00:00:00 /usr/local/redis3/bin/redis-server *:7114 [cluster] [root@edu-redis-05 cluster]# ps -ef | grep redis
root 5331 1 0 22:50 ? 00:00:00 /usr/local/redis3/bin/redis-server *:7115 [cluster] [root@edu-redis-06 cluster]# ps -ef | grep redis
root 5687 1 0 22:50 ? 00:00:00 /usr/local/redis3/bin/redis-server *:7116 [cluster]
注意:启动完毕后,6 个 Redis 实例尚未构成集群。
安装 ruby 和 rubygems(注意:需要 ruby 的版本在 1.8.7 以上)
# yum install ruby rubygems
检查 ruby 版本:
# ruby -v
ruby 1.8.7 (2013-06-27 patchlevel 374) [x86_64-linux]
gem 安装 redis ruby 接口:
# gem install redis
Successfully installed redis-3.2.1
1 gem installed
Installing ri documentation for redis-3.2.1...
Installing RDoc documentation for redis-3.2.1...
执行 Redis 集群创建命令(只需要在其中一个节点上执行一次则可)
# cd /usr/local/src/redis-3.0.3/src/
# cp redis-trib.rb /usr/local/bin/redis-trib
//replicas就是每个主节点有几个复制品,这里的关系是456是master加点,123是复制品。
# redis-trib create --replicas 1 192.168.1.114:7114 192.168.1.115:7115 192.168.1.116:7116 192.168.1.111:7111 192.168.1.112:7112 192.168.1.113:7113
//先看看能不能连上
>>> Creating cluster
Connecting to node 192.168.1.114:7114: OK
Connecting to node 192.168.1.115:7115: OK
Connecting to node 192.168.1.116:7116: OK
Connecting to node 192.168.1.111:7111: OK
Connecting to node 192.168.1.112:7112: OK
Connecting to node 192.168.1.113:7113: OK
>>> Performing hash slots allocation on 6 nodes...
//然后选三个作为master节点
Using 3 masters:
192.168.1.113:7113
192.168.1.112:7112
192.168.1.111:7111
//三个主节点的从节点分别是什么
Adding replica 192.168.1.116:7116 to 192.168.1.113:7113
Adding replica 192.168.1.115:7115 to 192.168.1.112:7112
Adding replica 192.168.1.114:7114 to 192.168.1.111:7111
S: 007a3fe8d7451d3d0a78fffd2653c8641809499c 192.168.1.114:7114
replicates 94e140b9ca0735040ae3428983835f1d93327aeb
S: ea69b6b6e2e7723eed50b1dabea9d244ccf3f098 192.168.1.115:7115
replicates c642b3071c4b2b073707ed3c3a2c16d53a549eff
S: 5f09dc0671732cf06a09f28631c90e0c68408520 192.168.1.116:7116
replicates 896a3c99da4fcf680de1f42406fccb551d8c40c3
M: 94e140b9ca0735040ae3428983835f1d93327aeb 192.168.1.111:7111
slots:10923-16383 (5461 slots) master
M: c642b3071c4b2b073707ed3c3a2c16d53a549eff 192.168.1.112:7112
slots:5461-10922 (5462 slots) master
M: 896a3c99da4fcf680de1f42406fccb551d8c40c3 192.168.1.113:7113
slots:0-5460 (5461 slots) master
Can I set the above configuration? (type 'yes' to accept): yes
(输入 yes 并按下回车确认之后,集群就会将配置应用到各个节点,并连接起(join)各个节点,也就是 让各个节点开始互相通讯)
>>> Nodes configuration updated
>>> Assign a different config epoch to each node
>>> Sending CLUSTER MEET messages to join the cluster
Waiting for the cluster to join....
>>> Performing Cluster Check (using node 192.168.1.114:7114) M: 007a3fe8d7451d3d0a78fffd2653c8641809499c 192.168.1.114:7114
slots: (0 slots) master
replicates 94e140b9ca0735040ae3428983835f1d93327aeb
M: ea69b6b6e2e7723eed50b1dabea9d244ccf3f098 192.168.1.115:7115
slots: (0 slots) master
replicates c642b3071c4b2b073707ed3c3a2c16d53a549eff
M: 5f09dc0671732cf06a09f28631c90e0c68408520 192.168.1.116:7116
slots: (0 slots) master
replicates 896a3c99da4fcf680de1f42406fccb551d8c40c3
M: 94e140b9ca0735040ae3428983835f1d93327aeb 192.168.1.111:7111
slots:10923-16383 (5461 slots) master
M: c642b3071c4b2b073707ed3c3a2c16d53a549eff 192.168.1.112:7112
slots:5461-10922 (5462 slots) master
M: 896a3c99da4fcf680de1f42406fccb551d8c40c3 192.168.1.113:7113
slots:0-5460 (5461 slots) master
上面输出信息的最后 slots中可以看到将16383个hash槽平均分到了三个节点上。
在cluster文件夹下面的7116文件夹下有两个配置文件nodes.conf和redis-7116.conf,前者是所有节点的信息,后者是某一个配置文件的详细配置信息。
一切正常的情况下输出以下信息:
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
最后一行信息表示集群中的 16384 个槽都有至少一个主节点在处理, 集群运作正常。
集群创建过程说明:
(1) 给定 redis-trib 程序的命令是 create , 这表示我们希望创建一个新的集群;
(2) 这里的 --replicas 1 表示每个主节点下有一个从节点;
(3) 之后跟着的其它参数则是实例的地址列表,程序使用这些地址所指示的实例来创建新集群; 总的来说,以上命令的意思就是让 redis-trib 程序创建一个包含三个主节点和三个从节点的集群。 接着,redis-trib 会打印出一份预想中的配置给你看,如果你觉得没问题的话(注意核对主从关系是否是 你想要的
),就可以输入 yes , redis-trib 就会将这份配置应用到集群当中。
使用 redis-cli 命令进入集群环境
[root@edu-redis-04 bin]# ./redis-cli -c -p 7114 127.0.0.1:7114> set wusc WuShuicheng
-> Redirected to slot [8559] located at 192.168.1.112:7112 OK
[root@edu-redis-01 bin]# ./redis-cli -c -p 7111 127.0.0.1:7111> get wusc
//通过key计算出hash值,将值的内容放到了对应的hash槽中,然后重定向到了对应的节点上去
//例如上面是在7114,这里拿值的时候重定向到了7112
-> Redirected to slot [8559] located at 192.168.1.112:7112
"WuShuicheng"
[root@edu-redis-02 bin]# ./redis-cli -c -p 7112
127.0.0.1:7112> get wusc
"WuShuicheng"
127.0.0.1:7112>
[root@edu-redis-01 bin]# ./redis-cli -p 7111 cluster nodes
查看节点的状态
cd /usr/local/redis3/bin
./redis-cli -p 7113 cluster nodes
(非伪集群适用,也就是每个节点都单独物理机部署的情况下
):
按上面的操作步骤,Redis 的启动脚本为:/usr/local/src/redis-3.0.3/utils/redis_init_script
将启动脚本复制到/etc/rc.d/init.d/目录下,并命名为 redis:
# cp /usr/local/src/redis-3.0.3/utils/redis_init_script /etc/rc.d/init.d/redis
编辑/etc/rc.d/init.d/redis,修改相应配置,使之能注册成为服务:
# vi /etc/rc.d/init.d/redis
#!/bin/sh
#
# Simple Redis init.d script conceived to work on Linux systems
# as it does use of the /proc filesystem.
REDISPORT=6379
EXEC=/usr/local/bin/redis-server
CLIEXEC=/usr/local/bin/redis-cli
PIDFILE=/var/run/redis_${REDISPORT}.pid
CONF="/etc/redis/${REDISPORT}.conf"
case "$1" in
start)
if [ -f $PIDFILE ]
then
echo "$PIDFILE exists, process is already running or crashed"
"WuShuicheng"
else
$EXEC $CONF
fi ;;
echo "Starting Redis server..."
stop)
if [ ! -f $PIDFILE ]
then
fi
;; *)
else
echo "$PIDFILE does not exist, process is not running"
PID=$(cat $PIDFILE)
echo "Stopping ..."
$CLIEXEC -p $REDISPORT shutdown
while [ -x /proc/${PID} ]
do
echo "Waiting for Redis to shutdown ..."
sleep 1 done
echo "Redis stopped"
echo "Please use start or stop as first argument"
;;
esac
查看以上 redis 服务脚本,关注标为橙色的几个属性,做如下几个修改的准备:
(1) 在脚本的第一行后面添加一行内容如下:
#chkconfig: 2345 80 90
(如果不添加上面的内容,在注册服务时会提示:service redis does not support chkconfig)
(2) REDISPORT 端口修改各节点对应的端口;(注意,端口名将与下面的配置文件名有关
)
(3) EXEC=/usr/local/bin/redis-server 改为 EXEC=/usr/local/redis3/bin/redis-server
(4) CLIEXEC=/usr/local/bin/redis-cli 改为 CLIEXEC=/usr/local/redis3/bin/redis-cli
(5) 配置文件设置,对 CONF 属性作如下调整:
CONF="/etc/redis/${REDISPORT}.conf"
改为 CONF="/usr/local/redis3/cluster/ R E D I S P O R T / r e d i s − {REDISPORT}/redis- REDISPORT/redis−{REDISPORT}.conf"
(6) 更改 redis 开启的命令,以后台运行的方式执行:
$EXEC $CONF & #“&”作用是将服务转到后面运行
修改后的/etc/rc.d/init.d/redis 服务脚本内容为(注意各节点的端口不同
):
#!/bin/sh
#chkconfig: 2345 80 90
#
# Simple Redis init.d script conceived to work on Linux systems # as it does use of the /proc filesystem.
REDISPORT=7111
EXEC=/usr/local/redis3/bin/redis-server
CLIEXEC=/usr/local/redis3/bin/redis-cli
PIDFILE=/var/run/redis-${REDISPORT}.pid
CONF="/usr/local/redis3/cluster/${REDISPORT}/redis-${REDISPORT}.conf "
case "$1" in
start)
if [ -f $PIDFILE ]
then
else
fi
;; stop)
echo "$PIDFILE exists, process is already running or crashed"
echo "Starting Redis server..."
$EXEC $CONF &
fi
;; *)
if [ ! -f $PIDFILE ]
then
else
echo "$PIDFILE does not exist, process is not running"
PID=$(cat $PIDFILE)
echo "Stopping ..."
$CLIEXEC -p $REDISPORT shutdown
while [ -x /proc/${PID} ]
do
echo "Waiting for Redis to shutdown ..."
sleep 1 done
echo "Redis stopped"
echo "Please use start or stop as first argument"
;;
esac
以上配置操作完成后,便可将 Redis 注册成为服务: # chkconfig --add redis
防火墙中打开对应的端口,各节点的端口不同(前面已操作则可跳过此步) # vi /etc/sysconfig/iptables
添加:
-A INPUT -m state --state NEW -m tcp -p tcp --dport 7111 -j ACCEPT
-A INPUT -m state --state NEW -m tcp -p tcp --dport 17111 -j ACCEPT
重启防火墙:
# service iptables restart
启动 Redis 服务
# service redis start
将 Redis 添加到环境变量中: # vi /etc/profile 在最后添加以下内容:
## Redis env
export PATH=$PATH:/usr/local/redis3/bin
使配置生效:
# source /etc/profile
现在就可以直接使用 redis-cli 等 redis 命令了:
关闭 Redis 服务
# service redis stop
默认情况下,Redis 未开启安全认证,可以通过/usr/local/redis3/cluster/7111/redis-7111.conf
的 requirepass 指定一个验证密码。
Redis 3.0 集群搭建测试(一):http://blog.csdn.net/zhu_tianwei/article/details/44928779 Redis 3.0 集群搭建测试(二):http://blog.csdn.net/zhu_tianwei/article/details/45009647 Redis 集群要点:http://5i.io/redis-3-0-cluster-configuration/
高可用架构篇
Redis 集群的高可用测试(含 Jedis 客户端的使用)
注意:本节教程内容紧接上一节教程《Dubbo 视频教程–高可用架构篇–第 05 节–Redis 集群的安装 (Redis3+CentOS)》的内容
1、 Jedis 客户端建议升级到最新版(当前为 2.7.3),这样对 3.0.x 集群有比较好的支持。
https://github.com/xetorthio/jedis
http://mvnrepository.com/artifact/redis.clients/jedis
2、 直接在 Java 代码中链接 Redis 集群:
// 数据库链接池配置
JedisPoolConfig config = new JedisPoolConfig();
config.setMaxTotal(100);
config.setMaxIdle(50);
config.setMinIdle(20);
config.setMaxWaitMillis(6 * 1000);
config.setTestOnBorrow(true);
// Redis集群的节点集合
Set<HostAndPort> jedisClusterNodes = new HashSet<HostAndPort>(); jedisClusterNodes.add(new HostAndPort("192.168.1.111", 7111)); jedisClusterNodes.add(new HostAndPort("192.168.1.112", 7112)); jedisClusterNodes.add(new HostAndPort("192.168.1.113", 7113)); jedisClusterNodes.add(new HostAndPort("192.168.1.114", 7114)); jedisClusterNodes.add(new HostAndPort("192.168.1.115", 7115)); jedisClusterNodes.add(new HostAndPort("192.168.1.116", 7116)); // 根据节点集创集群链接对象
//JedisCluster jedisCluster = new JedisCluster(jedisClusterNodes);
// 节点,客户端连接时的超时时间,最多重定向次数,链接池
JedisCluster jedisCluster = new JedisCluster(jedisClusterNodes, 2000, 100, config); int num = 1000;
String key = "wusc";
String value = "";
for (int i=1; i <= num; i++){
// 存数据
jedisCluster.set(key+i, "WuShuicheng"+i); // 取数据
value = jedisCluster.get(key+i); log.info(key+i + "=" + value);
// 删除数据 //jedisCluster.del(key+i);
//value = jedisCluster.get(key+i); //log.info(key+i + "=" + value);
}
上述代码中的redis重定向的意思,是因为在上面的安装中也有涉及到,即当redis查找的key的hash值的槽位不在当前的节点的时候,就会重定向到拥有这个槽位的节点上去,返回一个重定向的信息。
下面是一个MOVED例子:
get x
-move 3999 127.0.0.1:6381
3、Spring 配置 Jedis 链接 Redis3.0 集群的配置:
<!-- Jedis链接池配置,注意:Jedis版本建议升级到最新(当前最新版为2.7.2) -->
<bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig"> <property name="maxTotal" value="100" />
<property name="maxIdle" value="20" />
<property name="minIdle" value="10" />
<property name="blockWhenExhausted" value="true"></property> <property name="maxWaitMillis" value="3000" />
<property name="testOnBorrow" value="false" />
<property name="testOnReturn" value="false" />
<property name="testWhileIdle" value="true" />
<property name="minEvictableIdleTimeMillis" value="60000" /> <property name="timeBetweenEvictionRunsMillis" value="30000" /> <property name="numTestsPerEvictionRun" value="-1" />
</bean>
<!-- JedisCluster -->
<bean id="jedisCluster" class="redis.clients.jedis.JedisCluster">
<constructor-arg index="0"> <set>
<bean class="redis.clients.jedis.HostAndPort">
<constructor-arg index="0" value="192.168.1.111" />
<constructor-arg index="1" value="7111" type="int" />
</bean>
<bean class="redis.clients.jedis.HostAndPort">
<constructor-arg index="0" value="192.168.1.112" />
<constructor-arg index="1" value="7112" type="int" />
</bean>
<bean class="redis.clients.jedis.HostAndPort">
<constructor-arg index="0" value="192.168.1.113" />
<constructor-arg index="1" value="7113" type="int" />
</bean>
<bean class="redis.clients.jedis.HostAndPort">
<constructor-arg index="0" value="192.168.1.114" />
<constructor-arg index="1" value="7114" type="int" />
</bean>
<bean class="redis.clients.jedis.HostAndPort">
<constructor-arg index="0" value="192.168.1.115" />
<constructor-arg index="1" value="7115" type="int" />
</bean>
<bean class="redis.clients.jedis.HostAndPort">
<constructor-arg index="0" value="192.168.1.116" />
<constructor-arg index="1" value="7116" type="int" />
</bean>
</set> </constructor-arg>
<constructor-arg index="1" value="2000" type="int"></constructor-arg>
<constructor-arg index="2" value="100" type="int"></constructor-arg>
<constructor-arg index="3" ref="jedisPoolConfig"></constructor-arg>
</bean>
对应的 Java 调用代码样例(详细代码请看视频教程提供的 demo 源码):
JedisCluster jedisCluster = (JedisCluster) context.getBean("jedisCluster"); int num = 1000;
String key = "wusc";
String value = "";
for (int i=1; i <= num; i++){
// 存数据
jedisCluster.set(key+i, "WuShuicheng"+i); // 取数据
value = jedisCluster.get(key+i); log.info(key+i + "=" + value);
// 删除数据
//jedisCluster.del(key+i);
}
4、测试操作,请看视频教程。
一、Redis 集群特点
1、集群架构特点:
(1)所有的 redis 节点彼此互联(PING-PONG 机制),内部使用二进制协议优化传输速度和带宽;
(2)节点的 fail 是通过集群中超过半数的节点检测失效时才生效;
(3)客户端与 redis 节点直连,不需要中间 proxy 层。客户端不需要连接集群所有节点,连接集群中任何一 个可用节点即可;
(4)redis-cluster 把所有的物理节点映射到[0-16383]个 slot(哈希槽)上,cluster 负责维护
node<->slot<->value 。
2、集群选举容错:
(1)节点失效选举过程是集群中所有 master 参与,如果半数以上 master 节点与当前被检测 master 节点通 信检测超时(cluster-node-timeout),就认为当前 master 节点挂掉; (2):什么时候整个集群不可用(cluster_state:fail)?
A:如果集群任意 master 挂掉,且当前 master 没有 slave。集群进入 fail 状态,也可以理解成集群的 slot 映射[0-16383]不完整时进入 fail 状态。 ps : redis-3.0.0.rc1 加入 cluster-require-full- coverage 参数,默认关闭,打开集群兼容部分失败;
B:如果集群超过半数以上 master 挂掉,无论是否有 slave 集群进入 fail 状态。 ps:当集群不可用时, 所有对集群的操作做都不可用,收到((error) CLUSTERDOWN The cluster is down)错误。
二、客户端集群命令
集群
cluster info :打印集群的信息
cluster nodes :列出集群当前已知的所有节点(node),以及这些节点的相关信息。
节点
cluster meet :将 ip 和 port 所指定的节点添加到集群当中,让它成为集群的一份子。 cluster forget
cluster replicate
cluster saveconfig :将节点的配置文件保存到硬盘里面。
槽(slot)
cluster addslots [slot …] :将一个或多个槽(slot)指派(assign)给当前节点。 cluster delslots [slot …] :移除一个或多个槽对当前节点的指派。
cluster flushslots :移除指派给当前节点的所有槽,让当前节点变成一个没有指派任何槽的节点。 cluster setslot node
cluster setslot migrating
键
cluster keyslot :计算键 key 应该被放置在哪个槽上。
cluster countkeysinslot :返回槽 slot 目前包含的键值对数量。
cluster getkeysinslot :返回 count 个 slot 槽中的键。
三、集群高可用测试(主要看视频的操作与解说)
1、重建集群,步骤:
(1)关闭集群的各节点; ps -fef |grep redis kill -9
(2)删除各节点数据目录下的 nodes.conf、appendonly.aof、dump.rdb;
在/usr/local/redis3/cluster/7111
//这个命令可以直接将三个删除到。
# rm -rf appendonly.aof | rm -rf dump.rdb | rm -rf nodes.conf
(3)重新启用所有的节点
192.168.1.111
# /usr/local/redis3/bin/redis-server /usr/local/redis3/cluster/7111/redis-7111.conf 192.168.1.112
# /usr/local/redis3/bin/redis-server /usr/local/redis3/cluster/7112/redis-7112.conf
192.168.1.113
# /usr/local/redis3/bin/redis-server /usr/local/redis3/cluster/7113/redis-7113.conf
192.168.1.114
# /usr/local/redis3/bin/redis-server /usr/local/redis3/cluster/7114/redis-7114.conf
192.168.1.115
# /usr/local/redis3/bin/redis-server /usr/local/redis3/cluster/7115/redis-7115.conf
192.168.1.116
# /usr/local/redis3/bin/redis-server /usr/local/redis3/cluster/7116/redis-7116.conf
(4)执行集群创建命令(只需要在其中一个节点上执行一次则可)
# cd /usr/local/src/redis-3.0.3/src/
# cp redis-trib.rb /usr/local/bin/redis-trib
# redis-trib create --replicas 1 192.168.1.114:7114 192.168.1.115:7115 192.168.1.116:7116 192.168.1.111:7111 192.168.1.112:7112 192.168.1.113:7113
2、查看当前集群各节点的状态
[root@edu-redis-01 7111]# /usr/local/redis3/bin/redis-cli -c -p 7111
127.0.0.1:7111> cluster nodes
3、使用 demo 应用向集群写入 1000 个键值数据
使用 /usr/local/redis3/bin/redis-cli -c -p 711X 命令登录各节点,使用 keys * 查看各节点的所有 key
4、运行 demo 应用,获取所有的键值数据 如果有空值则停止
5、模拟集群节点宕机(实现故障转移,可重点看视频解说)
模拟用户不断的对redis进行操作
RedisClusterFailoverTest.java
/** 基于Dubbo的分布式系统架构视频教程,吴水成,[email protected],学习交流QQ群:367211134 **/
/**
*
* @描述: Redis集群测试 .
* @作者: WuShuicheng .
* @创建时间: 2015-3-23,上午1:30:40 .
* @版本号: V1.0 .
*/
public class RedisClusterFailoverTest {
private static final Log log = LogFactory.getLog(RedisClusterFailoverTest.class);
public static void main(String[] args) {
// 数据库链接池配置
JedisPoolConfig config = new JedisPoolConfig();
config.setMaxTotal(100);
config.setMaxIdle(50);
config.setMinIdle(20);
config.setMaxWaitMillis(6 * 1000);
config.setTestOnBorrow(true);
// Redis集群的节点集合
Set<HostAndPort> jedisClusterNodes = new HashSet<HostAndPort>();
jedisClusterNodes.add(new HostAndPort("192.168.1.111", 7111));
jedisClusterNodes.add(new HostAndPort("192.168.1.112", 7112));
jedisClusterNodes.add(new HostAndPort("192.168.1.113", 7113));
jedisClusterNodes.add(new HostAndPort("192.168.1.114", 7114));
jedisClusterNodes.add(new HostAndPort("192.168.1.115", 7115));
jedisClusterNodes.add(new HostAndPort("192.168.1.116", 7116));
try {
// 根据节点集创集群链接对象
//JedisCluster jedisCluster = new JedisCluster(jedisClusterNodes);
// 集群各节点集合,超时时间,最多重定向次数,链接池
JedisCluster jedisCluster = new JedisCluster(jedisClusterNodes, 2000, 100, config);
int num = 1000;
String key = "wusc";
String value = "";
int count = 1;
while(true){
for (int i=1; i <= num; i++){
try {
// 存数据
//jedisCluster.set(key+i, "WuShuicheng"+i);
// 取数据
value = jedisCluster.get(key+i);
log.info(key+i + "=" + value);
//如果取出的是空值,就会跳出这个循环、
if (value == null || "".equals(value)){
log.error("===>break" + key+i + " value is null");
break;
}
} catch (Exception e) {
//出现问题就自动重连
log.error("====>", e);
Thread.sleep(3000);
continue;
}
// 删除数据
//jedisCluster.del(key+i);
//value = jedisCluster.get(key+i);
//log.info(key+i + "=" + value);
}
log.info("===================================>count:" + count);
if (value == null || "".equals(value)){
break;
}
//统计执行了几次1000
count++;
Thread.sleep(1000);
}
} catch (Exception e) {
log.error("====>", e);
}
}
}
(1)Jedis 客户端循环操作集群数据(模拟用户持续使用系统)
(2)查看 Redis 集群当前状态(用于接下来做节点状态变化对比)
(3)关闭其中一个 master 节点(7111) ps -ef |grep =====> kill
(4)观察该 master 节点和对应的 slave 节点的状态变化(请看视频解说)
节点状态 fail? 表示正在判断是否失败
节点状态 fail 表示节点失败,对应的 slave 节点提升为 master
(5)再查看集群状态变化# /usr/local/src/redis-3.0.3/src/redis-trib.rb check 192.168.1.116:7116
由上可见,7114 节点替换 7111,由 slave 变成了 master,原来的7111已经从展示中去除掉了
此时再执行 demo 应用获取所有的键值数据,依然正常,说明 slave 替换 master 成功,集群正常。
6、恢复 fail 节点
(1)启动 7111
# /usr/local/redis3/bin/redis-server /usr/local/redis3/cluster/7111/redis-7111.conf
(2)查看集群状态
其中 7111 变成 7114 的 slave
7、观察集群节点切换过程中,对客户端的影响
JedisCluster 链接 Redis 集群操作时遇到的几个常见异常:
(1)重定向次数过多
redis.clients.jedis.exceptions.JedisClusterMaxRedirectionsException: Too many Cluster redirections?
解决方法: 初始化 JedisCluster 时,设定 JedisCluster 的 maxRedirections //集群各节点集合,超时时间(默认 2 秒),最多重定向次数(默认 5),链接池
new JedisCluster(jedisClusterNodes, 2000, 100, config);
(2)集群不可以用
redis.clients.jedis.exceptions.JedisClusterException: CLUSTERDOWN The cluster is down 原因:集群节点状态切换过程中会出现临时闪断,客户端重试操作则可。
即上面的代码中,如果抛出异常直接cotinue了,下次取数据的时候绕过了刚刚本来要取的数据,可以再下次取的时候,在取一次这个。
(3)链接超时
redis.clients.jedis.exceptions.JedisConnectionException: java.net.SocketTimeoutException: Read timed out 解决方法: 初始化 JedisCluster 时,设定 JedisCluster 的 timeout(默认为两秒);也可以修改源码中的 默认时间。
8、总结:
优点:
在 master 节点下线后,slave 节点会自动提升为 master 节点,保存集群持续提供服务;
fail 节点恢复后,会自动添加到集群中,变成 slave 节点;
缺点:
由于 redis 的复制使用异步机制,在自动故障转移的过程中,集群可能会丢失写命令。
然而 redis 几 乎是同时执行(将命令恢复发送给客户端,以及将命令复制到 slave 节点)这两个操作,所以实际中,命令 丢失的窗口非常小。
注意:本节教程内容紧接上两节教程《高可用架构篇–第 05 节–Redis 集群的安装(Redis3+CentOS)》和《高可用架构篇–第 06 节–Redis 集群的高可用测试(含 Jedis 客户端的使用)》的内容
主机名 | IP | 服务端口 默认 6379 | 集群端口 服务端口值+10000 | 主/从 |
---|---|---|---|---|
edu-redis-07 | 192.168.1.117 | 7117 | 17117 | Master |
edu-redis-07 | 192.168.1.117 | 7118 | 17118 | Slave |
按规划:在 192.168.1.117 的防火墙中打开相应的端口
-A INPUT -m state --state NEW -m tcp -p tcp --dport 7117 -j ACCEPT
-A INPUT -m state --state NEW -m tcp -p tcp --dport 17117 -j ACCEPT
-A INPUT -m state --state NEW -m tcp -p tcp --dport 7118 -j ACCEPT
-A INPUT -m state --state NEW -m tcp -p tcp --dport 17118 -j ACCEPT
可请参考《高可用架构篇–第 05 节–Redis 集群的安装(Redis3+CentOS)》教程的文档,命令如下: # yum install gcc tcl
# cd /usr/local/src
# wget http://download.redis.io/releases/redis-3.0.3.tar.gz
# mkdir /usr/local/redis3
# tar -zxvf redis-3.0.3.tar.gz
# cd redis-3.0.3
# make PREFIX=/usr/local/redis3 install
# yum install ruby rubygems
# gem install redis
192.168.1.117
# mkdir -p /usr/local/redis3/cluster/7117
# mkdir -p /usr/local/redis3/cluster/7118
# cp /usr/local/src/redis-3.0.3/redis.conf /usr/local/redis3/cluster/7117/redis-7117.conf
# cp /usr/local/src/redis-3.0.3/redis.conf /usr/local/redis3/cluster/7118/redis-7118.conf
提示:conf 配置文件具体内容请看教程提供的 redis-7117.conf 和 redis-7118.conf 配置文件,主要增 加了数据目录 dir 属性的配置。
# /usr/local/redis3/bin/redis-server /usr/local/redis3/cluster/7117/redis-7117.conf
# /usr/local/redis3/bin/redis-server /usr/local/redis3/cluster/7118/redis-7118.conf
# ps -ef | grep redis
root 4865 1 0 01:01 ? 00:00:00 /usr/local/redis3/bin/redis-server *:7117 [cluster]
root 4869 1 0 01:01 ? 00:00:00 /usr/local/redis3/bin/redis-server *:7118 [cluster]
[root@edu-redis-01 src]# /usr/local/src/redis-3.0.3/src/redis-trib.rb Usage: redis-trib <command> <options> <arguments ...>
import
set-timeout
del-node
create
help
add-node
reshard
fix
check
call
host:port
--from <arg>
host:port milliseconds
host:port node_id
host1:port1 ... hostN:portN
--replicas <arg>
(show this help)
new_host:new_port existing_host:existing_port
--slave
--master-id <arg>
host:port
--slots <arg>
--to <arg>
--yes
--from <arg>
host:port
host:port
host:port command arg arg .. arg
For check, fix, reshard, del-node, set-timeout you can specify the host and port of any working node in the cluster.
redis-trib.rb 命令参数说明:
call:执行 redis 命令
create:创建一个新的集群(上一节教程有介绍)
add-node:将一个节点添加到集群里面,第一个是新节点 ip:port, 第二个是集群中任意一个正常节点 ip:port,–master-id
reshard:重新分片 check:查看集群信息
del-node:移除一个节点
add-node 将一个节点添加到集群里面, 第一个是新节点 ip:port,第二个是任意一个已存在节点 ip:port
# /usr/local/src/redis-3.0.3/src/redis-trib.rb add-node 192.168.1.117:7117 192.168.1.111:7111
>>> Adding node 192.168.1.117:7117 to cluster 192.168.1.111:7111
Connecting to node 192.168.1.111:7111: OK
Connecting to node 192.168.1.116:7116: OK Connecting to node 192.168.1.113:7113: OK Connecting to node 192.168.1.112:7112: OK Connecting to node 192.168.1.115:7115: OK Connecting to node 192.168.1.114:7114: OK
>>> Performing Cluster Check (using node 192.168.1.111:7111) M: cc50047487b52697d62b1a72b231b7c74e08e051 192.168.1.111:7111
slots:10923-16383 (5461 slots) master
1 additional replica(s)
S: b21ae6d0a3e614e53bbc52639173ec3ad68044b5 192.168.1.116:7116
slots: (0 slots) slave
replicates 041addd95fa0a15d98be363034e53dd06f69ef47
M: 041addd95fa0a15d98be363034e53dd06f69ef47 192.168.1.113:7113
slots:0-5460 (5461 slots) master
1 additional replica(s)
M: 712e523b617eea5a2ed8df732a50ff298ae2ea48 192.168.1.112:7112
slots:5461-10922 (5462 slots) master
1 additional replica(s)
S: 55c0db5af1b917f3ce0783131fb8bab28920e1f3 192.168.1.115:7115
slots: (0 slots) slave
replicates 712e523b617eea5a2ed8df732a50ff298ae2ea48
S: 8a6ca1452d61f8b4726f0649e6ce49a6ec4afee2 192.168.1.114:7114
slots: (0 slots) slave
replicates cc50047487b52697d62b1a72b231b7c74e08e051 [OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
Connecting to node 192.168.1.117:7117: OK
>>> Send CLUSTER MEET to node 192.168.1.117:7117 to make it join the cluster. [OK] New node added correctly.
通过连接到redis数据库(任意一个节点)执行cluster nodes命令,能查看集群node转状态
以上操作结果表示节点添加成功,新增的节点不包含任何数据, 因为它没有分配任何 slot。 新加入的节点是一个 master 节点,当集群需要将某个从节点升级为新的主节点时,这个新节点不会被选 中。
为新节点分配哈希槽(slot):
你只需要指定集群中其中一个节点的地址,redis-trib 就会自动找到集群中的其他节点。目前 redis-trib 只能在管理员的协助下完成重新分片的工作,命令如下:
# /usr/local/src/redis-3.0.3/src/redis-trib.rb reshard 192.168.1.111:7111
Connecting to node 192.168.1.111:7111: OK
Connecting to node 192.168.1.117:7117: OK
Connecting to node 192.168.1.116:7116: OK
Connecting to node 192.168.1.113:7113: OK
Connecting to node 192.168.1.112:7112: OK
Connecting to node 192.168.1.115:7115: OK
Connecting to node 192.168.1.114:7114: OK
>>> Performing Cluster Check (using node 192.168.1.111:7111)
M: cc50047487b52697d62b1a72b231b7c74e08e051 192.168.1.111:7111
slots:10923-16383 (5461 slots) master
1 additional replica(s)
M: badbc0ffde2a3700df7e179d23fa2762108eabba 192.168.1.117:7117
slots: (0 slots) master
0 additional replica(s)
S: b21ae6d0a3e614e53bbc52639173ec3ad68044b5 192.168.1.116:7116
slots: (0 slots) slave
replicates 041addd95fa0a15d98be363034e53dd06f69ef47
M: 041addd95fa0a15d98be363034e53dd06f69ef47 192.168.1.113:7113
slots:0-5460 (5461 slots) master
1 additional replica(s)
M: 712e523b617eea5a2ed8df732a50ff298ae2ea48 192.168.1.112:7112
slots:5461-10922 (5462 slots) master
1 additional replica(s)
S: 55c0db5af1b917f3ce0783131fb8bab28920e1f3 192.168.1.115:7115
slots: (0 slots) slave
replicates 712e523b617eea5a2ed8df732a50ff298ae2ea48
S: 8a6ca1452d61f8b4726f0649e6ce49a6ec4afee2 192.168.1.114:7114
slots: (0 slots) slave
replicates cc50047487b52697d62b1a72b231b7c74e08e051 [OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
How many slots do you want to move (from 1 to 16384)? 500
上面的提示是确认你打算移动的哈希槽 slots 的数量(这里槽数量设置为 500)
除了移动的哈希槽数量之外, redis-trib 还需要知道重新分片的目标(target node), 也就是负责接收 这 500 个哈希槽的节点。指定目标需要使用节点的 ID , 而不是 IP 地址和端口。我们打算向上面新增 的主节点来作为目标, 它的 IP 地址和端口是 192.168.1.117:7117,而节点 ID 则是 badbc0ffde2a3700df7e179d23fa2762108eabba , 那么我们应该向 redis-trib 提供节点的 ID :
What is the receiving node ID? badbc0ffde2a3700df7e179d23fa2762108eabba
接下来 redis-trib 会向你询问重新分片的源节点(source node), 也就是要从哪个节点中取出 500 个 哈希槽,并将这些槽移动到目标节点上面。如果我们不打算从特定的节点上取出指定数量的哈希槽, 那么 可以向 redis-trib 输入 all , 这样的话, 集群中的所有主节点都会成为源节点, redis-trib 将从各 个源节点中各取出一部分哈希槽, 凑够 500 个, 然后移动到目标节点上面:
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
输入 all 并按下回车之后, redis-trib 将打印出哈希槽的移动计划:
Do you want to proceed with the proposed reshard plan (yes/no)? yes
如果你觉得没问题的话, 就可以输入 yes 并再次按回车确认, redis-trib 就会正式开始执行重新分片 操作, 将指定的哈希槽从源节点一个个地移动到目标节点上面。
注意:可以同步观察重新分片是否会对客户端的连续使用产生影响(结果:不影响,通过上节中的demo:RedisClusterFailoverTest得出此结论)。
移动前,7117 上没有 slot:
移动后,7117 上有 3 段 slot:
在重新分片操作执行完毕之后, 可以使用以下命令来检查集群是否正常:
# /usr/local/src/redis-3.0.3/src/redis-trib.rb check 192.168.1.111:7111
上面输出的检查结果显示,重新分片成功,集群状态正常。也可以用以下命令再次查看集群的节点状况:
# /usr/local/redis3/bin/redis-cli -c -p 7111 cluster nodes
以上集群状态输出信息解析:
(1)节点 ID
(2)IP:PORT
(3)节点状态标识: master、slave、myself、fail?、fail (4)如果是从节点,表示主节点的 ID;如果是主节点,则为 ‘-’ (5)集群最近一次向各个节点发送 PING 命令后,过去多长时间还没有接到回复 (6)节点最近一次返回 PONG 的时间戳
(7)节点的配置纪元
(8)本节点的网络连接情况: connected、disconnected (9)如果是主节点,表示节点包含的槽
(1)添加节点
#/usr/local/src/redis-3.0.3/src/redis-trib.rb add-node 192.168.1.117:7118 192.168.1.111:7111 >>> Adding node 192.168.1.117:7118 to cluster 192.168.1.111:7111
Connecting to node 192.168.1.111:7111: OK
Connecting to node 192.168.1.117:7117: OK
Connecting to node 192.168.1.116:7116: OK
Connecting to node 192.168.1.113:7113: OK
Connecting to node 192.168.1.112:7112: OK
Connecting to node 192.168.1.115:7115: OK
Connecting to node 192.168.1.114:7114: OK
>>> Performing Cluster Check (using node 192.168.1.111:7111)
M: cc50047487b52697d62b1a72b231b7c74e08e051 192.168.1.111:7111
slots:11089-16383 (5295 slots) master
1 additional replica(s)
M: badbc0ffde2a3700df7e179d23fa2762108eabba 192.168.1.117:7117
slots:0-165,5461-5627,10923-11088 (499 slots) master
0 additional replica(s)
S: b21ae6d0a3e614e53bbc52639173ec3ad68044b5 192.168.1.116:7116
slots: (0 slots) slave
replicates 041addd95fa0a15d98be363034e53dd06f69ef47
M: 041addd95fa0a15d98be363034e53dd06f69ef47 192.168.1.113:7113
slots:166-5460 (5295 slots) master
1 additional replica(s)
M: 712e523b617eea5a2ed8df732a50ff298ae2ea48 192.168.1.112:7112
slots:5628-10922 (5295 slots) master
1 additional replica(s)
S: 55c0db5af1b917f3ce0783131fb8bab28920e1f3 192.168.1.115:7115
slots: (0 slots) slave
replicates 712e523b617eea5a2ed8df732a50ff298ae2ea48
S: 8a6ca1452d61f8b4726f0649e6ce49a6ec4afee2 192.168.1.114:7114
slots: (0 slots) slave
replicates cc50047487b52697d62b1a72b231b7c74e08e051
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
Connecting to node 192.168.1.117:7118: OK
>>> Send CLUSTER MEET to node 192.168.1.117:7118 to make it join the cluster.
[OK] New node added correctly.
新增的 7118 为一个 master
(2)redis-cli 连接上新节点 shell,输入命令:cluster replicate 对应 master 的 node-id
# /usr/local/redis3/bin/redis-cli -c -p 7118
127.0.0.1:7118>cluster replicate ab31611b3424990e2b9bbe73135cb4cb0ace394f
OK
ab31611b3424990e2b9bbe73135cb4cb0ace394f就是上面的7117
在线添加 slave 时,需要 dump 整个 master 进程,并传递到 slave,再由 slave 加载 rdb 文件到内存,rdb 传输过程中 Master 可能无法提供服务,整个过程消耗大量 IO,因此要小心操作。尽量选择用户低峰值的时候。
查看执行结果:
127.0.0.1:7116> cluster nodes
这时 7117 已变成 7118 的 slave
对于负载/数据不均匀的情况,可以在线reshard slot来解决,方法与添加新master的reshard一样,只 是需要 reshard 的 master 节点是老节点。
# cd /usr/local/src/redis-3.0.3/src/
//这里注意是脚本del-node,后面的两个参数依次是slave节点的ip端口和,id
# ./redis-trib.rb del-node 192.168.1.117:7118 5256e05a17c106c93285a03aff1b1b9e7ca7bf0c
这时候,用下以下再查看集群状态,会发现该 slave 节点已成功移除
# /usr/local/redis3/bin/redis-cli -c -p 7116 cluster nodes
删除 master 节点之前首先要使用 reshard 移除该 master 的全部 slot,然后再删除当前节点(目前只能把被 删除 master 的 slot 迁移到一个节点上),操作和分配 slot 类似,指定具体的 Source node 即可。
# /usr/local/src/redis-3.0.3/src/redis-trib.rb reshard 192.168.1.117:7117
Connecting to node 192.168.1.117:7117: OK
Connecting to node 192.168.1.112:7112: OK
Connecting to node 192.168.1.115:7115: OK
Connecting to node 192.168.1.114:7114: OK
Connecting to node 192.168.1.111:7111: OK
Connecting to node 192.168.1.116:7116: OK
Connecting to node 192.168.1.113:7113: OK
>>> Performing Cluster Check (using node 192.168.1.117:7117)
M: ab31611b3424990e2b9bbe73135cb4cb0ace394f 192.168.1.117:7117
slots:0-165,5461-5627,10923-11088 (499 slots) master
0 additional replica(s)
M: d2c6c159b07e8197e2c8d2eae8c847050159f602 192.168.1.112:7112
slots:5628-10922 (5295 slots) master
1 additional replica(s)
S: f34b28f1483f0c0d9543e93938fc12b8818050cb 192.168.1.115:7115
slots: (0 slots) slave
replicates d2c6c159b07e8197e2c8d2eae8c847050159f602
M: 48db78bcc55c4c3a3788940a6458b921ccf95d44 192.168.1.114:7114
slots:11089-16383 (5295 slots) master
1 additional replica(s)
S: 8dd55e9b4da9f62b9b15232e86553f1337864179 192.168.1.111:7111
slots: (0 slots) slave
replicates 48db78bcc55c4c3a3788940a6458b921ccf95d44
S: 1fd90d54090925afb4087d4ef94a1710a25160d6 192.168.1.116:7116
slots: (0 slots) slave
replicates 4e46bd06654e8660e617f7249fa22f6fa1fdff0d
M: 4e46bd06654e8660e617f7249fa22f6fa1fdff0d 192.168.1.113:7113
slots:166-5460 (5295 slots) master
1 additional replica(s)
[OK] All nodes agree about slots configuration. >>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
//输入被删除 master 的所有 slot 数量
How many slots do you want to move (from 1 to 16384)? 499
//接收 slot 的 master 节点 ID
What is the receiving node ID? 48db78bcc55c4c3a3788940a6458b921ccf95d44 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.
//准备被删除 master 节点的 node-id
Source node #1: ab31611b3424990e2b9bbe73135cb4cb0ace394f Source node
#2:done
Ready to move 499 slots.
Source nodes:
M: ab31611b3424990e2b9bbe73135cb4cb0ace394f 192.168.1.117:7117
slots:0-165,5461-5627,10923-11088 (499 slots) master
0 additional replica(s)
Destination node:
M: 48db78bcc55c4c3a3788940a6458b921ccf95d44 192.168.1.114:7114
slots:11089-16383 (5295 slots) master
1 additional replica(s)
Resharding plan:
Moving slot 0 from ab31611b3424990e2b9bbe73135cb4cb0ace394f Moving slot 1 from ab31611b3424990e2b9bbe73135cb4cb0ace394f ......
//输入 yes 执行 reshard
Do you want to proceed with the proposed reshard plan (yes/no)? yes
移除该 Master 节点的所有 slot 后,重新查看集群状态,会发现该节点不再占用 slot:
# /usr/local/redis3/bin/redis-cli -c -p 7116 cluster nodes
可以发现是master节点,但是后面没有槽位
确认已清空该 Master 节点的所有 slot 后就可以删除该节点了(命令与删除 slave 节点一样):
# cd /usr/local/src/redis-3.0.3/src/
# ./redis-trib.rb del-node 192.168.1.117:7117 ab31611b3424990e2b9bbe73135cb4cb0ace394f