Redis部署手记之集群模式

1.1简介
Redis 在 3.0 版本后开始支持集群模式(Redis Cluster),目前官方一直都在维护中,具有代
表性,建议优先使用。
Redis Cluster 是一种服务器 Sharding 技术,只要将查询请求发送到 Redis Cluster 中的任意节
点,接收到请求的节点会就将查询请求发送到正确的节点上执行:
当 Redis 客户端操作的 key 恰好在所查询的 Redis 节点上时,就像操作 Redis 单例一样。
当客户端操作的 key 没有分配到所查询的 Redis 节点上时,Redis 会返回转向指令,指向正确的Redis 节点,这类似于浏览器页面的 302 重定向跳转。

Redis Cluster 并没有使用一致性 Hash,而是采用 slot(槽)的概念,一共分成 16384 个槽。
Redis Cluster 要保证 16384 个槽对应的 Redis 节点都正常工作,否则一旦某个 Redis 节点发生故障,那它负责的 slots 也就失效,整个集群将不能工作。
为了增加集群的高可用性,官方推荐的方案是将 Redis 节点配置成主从结构,即一个主节点,挂 N 个从节点。这时,如果主节点失效,Redis Cluster 会根据选举算法在从节点中选择一个上升为主节点,整个集群继续对外提供服务。

Redis Cluster 的架构模式如下图:

在这里插入图片描述
在这里插入图片描述
上图描述的是六个 redis 实例构成的集群(三主三从),其中:

  • 6379 端口为客户端通讯端口
  • 16379 端口为集群总线端口
  • 集群内部划分为 16384 个数据分槽,分布在三个主节点中
  • 从节点没有分槽,不会参与集群投票,也不会提供数据读取服务,仅作为主节点的备份
  • 三个主节点中平均分布着 16384 数据分槽的三分之一,每个节点中不会存有有重复数据,仅仅使用自己的从机帮忙冗余

Redis Cluster 具有以下优点:

  • 无中心架构,支持动态扩容,对业务透明
  • 具备 Sentinel 的监控和自动故障迁移能力
  • 高性能,客户端直连 Redis 服务,免去了 Proxy 代理的损耗
  • 客户端不需要连接集群所有节点,连接集群中任何一个可用节点即可(实际上是必须连接整个集,避免单点故障导致客户端不可用)

Redis Cluster 同时也具有以下缺点:

  • 部署和运维复杂
  • 数据迁移需要人工干预
  • 只能使用 0 号数据库
  • 不支持批量操作
  • 分布式逻辑和存储模块耦合

1.2部署
下面署集群模式,官方推荐 Redis Cluster 至少需要六个节点,即三主三从。
这里准备六个 Redis 节点,同时为了区分主从/哨兵模式的部署位置,顺便拷贝一下 Redis 集群节点的目录(以节点端口号区分):

#mkdir /usr/local/redis-cluster            # 创建 Redis 集群目录
#cp /usr/local/redis-4.0.11/src/redis-trib.rb /usr/local/redis-cluster/   # 集群构建脚本
#cp -r /usr/local/redis-4.0.11 /usr/local/redis-cluster/redis-6390     # Redis 集群节点目录
#cp -r /usr/local/redis-4.0.11 /usr/local/redis-cluster/redis-6391     # Redis 集群节点目录
#cp -r /usr/local/redis-4.0.11 /usr/local/redis-cluster/redis-6392     # Redis 集群节点目录
#cp -r /usr/local/redis-4.0.11 /usr/local/redis-cluster/redis-6393     # Redis 集群节点目录
#cp -r /usr/local/redis-4.0.11 /usr/local/redis-cluster/redis-6394     # Redis 集群节点目录
#cp -r /usr/local/redis-4.0.11 /usr/local/redis-cluster/redis-6395     # Redis 集群节点目录

接下来会基于/usr/local/redis-cluster/redis-xxxx目录部署集群模式。
然后配置这六个 Redis 节点的配置文件,其配置基本相同(主从无需配置,在后面初次构建集群时会自动分配,而以后添加新节点,可人工指定是主节点还是从节点):

角色 配置文件 服务端口
集群节点 /usr/local/redis-cluster/redis-6390/redis.conf 6390
集群节点 /usr/local/redis-cluster/redis-6391/redis.conf 6391
集群节点 /usr/local/redis-cluster/redis-6392/redis.conf 6392
集群节点 /usr/local/redis-cluster/redis-6393/redis.conf 6393
集群节点 /usr/local/redis-cluster/redis-6394/redis.conf 6394
集群节点 /usr/local/redis-cluster/redis-6395/redis.conf 6395

配置文件/usr/local/redis-cluster/redis-639x/redis.conf的内容如下(仅端口号不同):

bind 127.0.0.1                  # 正式部署请设为合适的 IP
port 639x
daemonize yes
pidfile /var/run/redis_639x.pid
dir /tmp/redis-cluster          # Redis 的工作目录(若不存在需手建否则无法启动),logfile 与 dbfilename 受其影响
logfile "639x.log"              # Redis 日志名称(默认不配置,表示输出到 stdout),正式部署请设置为合适的名称
dbfilename dump-639x.rdb        # Redis 数据持久化时的存储位置,正式部署请设置为合适的名称
cluster-enabled yes             # 启用集群模式
cluster-config-file nodes-639x.conf    # 集群节点的配置文件,由集群创建,但若同一台主机上的名称需唯一
cluster-node-timeout 15000

新建上面配置的 Redis 集群工作目录:

#mkdir /tmp/redis-cluster

然后使用 redis-server命令启动六个 Redis 节点:

#cd /usr/local/redis-cluster/redis-6390/src/     # 切换到 Redis-6390 节点的启动脚本目录
#./redis-server ../redis.conf                    # 启动 Redis 节点


#cd /usr/local/redis-cluster/redis-6391/src/
#./redis-server ../redis.conf


#cd /usr/local/redis-cluster/redis-6392/src/
#./redis-server ../redis.conf


#cd /usr/local/redis-cluster/redis-6393/src/
#./redis-server ../redis.conf


cd /usr/local/redis-cluster/redis-6394/src/
./redis-server ../redis.conf


#cd /usr/local/redis-cluster/redis-6395/src/
#./redis-server ../redis.conf

先通过 ps -ef|grep redis命令可查看六个节点进程是否正常启动:
在这里插入图片描述
然后使用 Redis 官方提供的工具redis-trib.rb(注意:这个是 ruby 脚本,需要安装相关支持库,否则无法运行)把这 6 个节点组建成集群(注意:若集群节点分布在多台不同的机器上,只需其中一个机器执行这条命令即可,但要包含所有机器的集群节点):

#cd /usr/local/redis-cluster/     # 切换到集群构建脚本目录
#./redis-trib.rb create --replicas 1 127.0.0.1:6390 127.0.0.1:6391 127.0.0.1:6392 127.0.0.1:6393 127.0.0.1:6394 127.0.0.1:6395

注:若以后机器 IP 发生变更,需要重新执行此命令重建集群(重建集群需要先停止集群中所有节点进程,然后删除原集群中所有节点的 node.conf 文件,最后按照上文步骤构建集群即可)。

此时 Redis 会返回正在构建的集群信息,返回的信息大概意思是“正在把 slots 槽位分配到 6个节点的集群,其中 6390、6391、6392 是主节点,6394 是 6390 的从节点,6395 是 6391 的从节点,6393 是 6392 的从节点”。
这里的主从分配是自动的,若确认无误则输入 yes:

[root@localhost redis-cluster]# ./redis-trib.rb create --replicas 1 127.0.0.1:6390 127.0.0.1:6391 127.0.0.1:6392 127.0.0.1:6393 127.0.0.1:6394 127.0.0.1:6395
>>> Creating cluster
>>> Performing hash slots allocation on 6 nodes...
Using 3 masters:        #3个主节点
127.0.0.1:6390
127.0.0.1:6391
127.0.0.1:6392
Adding replica 127.0.0.1:6394 to 127.0.0.1:6390    #3个从节点
Adding replica 127.0.0.1:6395 to 127.0.0.1:6391
Adding replica 127.0.0.1:6393 to 127.0.0.1:6392
>>> Trying to optimize slaves allocation for anti-affinity
[WARNING] Some slaves are in the same host as their master
M: c736fda2d0b86917b26ae6cbf1d2a100dfdf9b1d 127.0.0.1:6390
   slots:0-5460 (5461 slots) master
M: a81920b65349ca9e49b4ba02cc276f7e7fffba94 127.0.0.1:6391
   slots:5461-10922 (5462 slots) master
M: 4795c10b891f214c147f779b222745ad71231340 127.0.0.1:6392
   slots:10923-16383 (5461 slots) master
S: cfb902bc8c6ed5bd548cc9aadcaea124944eff1a 127.0.0.1:6393
   replicates a81920b65349ca9e49b4ba02cc276f7e7fffba94
S: 15bbc002ac6103dfdcc61fba24f42aaf84a306ad 127.0.0.1:6394
   replicates 4795c10b891f214c147f779b222745ad71231340
S: 38da95e8ccf14b6b6990398c32fef39b68e7b3fe 127.0.0.1:6395
   replicates c736fda2d0b86917b26ae6cbf1d2a100dfdf9b1d
Can I set the above configuration? (type 'yes' to accept): yes      # 若确认无误则输入 yes

若构建集群成功,则会返回集群内这 6 个节点的信息:
>>> 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 127.0.0.1:6390)
M: c736fda2d0b86917b26ae6cbf1d2a100dfdf9b1d 127.0.0.1:6390
   slots:0-5460 (5461 slots) master
   1 additional replica(s)
S: cfb902bc8c6ed5bd548cc9aadcaea124944eff1a 127.0.0.1:6393
   slots: (0 slots) slave
   replicates a81920b65349ca9e49b4ba02cc276f7e7fffba94
M: 4795c10b891f214c147f779b222745ad71231340 127.0.0.1:6392
   slots:10923-16383 (5461 slots) master
   1 additional replica(s)
S: 15bbc002ac6103dfdcc61fba24f42aaf84a306ad 127.0.0.1:6394
   slots: (0 slots) slave
   replicates 4795c10b891f214c147f779b222745ad71231340
S: 38da95e8ccf14b6b6990398c32fef39b68e7b3fe 127.0.0.1:6395
   slots: (0 slots) slave
   replicates c736fda2d0b86917b26ae6cbf1d2a100dfdf9b1d
M: a81920b65349ca9e49b4ba02cc276f7e7fffba94 127.0.0.1:6391
   slots:5461-10922 (5462 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.

1.3 测试
现在测试 Redis Cluster 是否能正常工作。
通过 Redis 测试客户端命令redis-cli 连接到集群任意一个节点:

#cd /usr/local/redis-4.0.11/src/        # 切换到启动脚本目录
#./redis-cli -c -h 127.0.0.1 -p 6390    # 以集群方式连接到 Redis-6390 节点

在这里插入图片描述
这里需要注意,与前面单机/主从/哨兵模式不同的是,客户端命令 redis-cli 需要增加一个-c参数,表示是连接到集群,这样客户端的读写行为才是在整个集群中可见的。
若不加-c参数虽然也可连接,但是仅仅是连接到当前的节点,是无法进行数据读写的(除非所读写的数据的键值,经过 Hash 计算得到的 slot 槽号,刚好在这个节点里面)。

现测试6390 节点执行get 命令,获取键名为name的数据:

127.0.0.1:6390> get name
-> Redirected to slot [5798] located at 127.0.0.1:6391
(nil)           # 由于是新部署的 Redis 集群,该键值必定不存在

然后在 6390 节点写入数据(执行 set 命令,为键name设置数据):

127.0.0.1:6390> set name jingjing
-> Redirected to slot [5798] located at 127.0.0.1:6391
OK

再通过 Redis 测试客户端命令redis-cli 连接到集群另一个节点:

#cd /usr/local/redis-4.0.11/src/          # 切换到启动脚本目录
#./redis-cli -c -h 127.0.0.1 -p 6393      # 以集群方式连接到 Redis-6393节点

在 6393节点执行get 命令,获取键名为name的数据(经过集群节点转发请求,可以取到数据):

127.0.0.1:6393> get name
-> Redirected to slot [5798] located at 127.0.0.1:6391
"jingjing"

现在尝试一下不以集群方式连接到节点 6393,看一下会发生什么:

[root@localhost src]# ./redis-cli -h 127.0.0.1 -p 6393    # 以单点方式连接到 Redis-6393节点(无-c 参数)
127.0.0.1:6393> get name             # 获取键值为name的数据
(error) MOVED 5798 127.0.0.1:6391    # 报错:该数据在 63921节点的5798槽位
127.0.0.1:6393> set name wen         # 设置键为name的值
(error) MOVED 5798 127.0.0.1:6391    # 报错:该键应该设置到6391节点的5798槽位

# 有些版本的报错信息是这样的,意思是相同的:
# -> Redirected to slot [5798] located at 127.0.0.1:6391

之所以会发生这种情况,其实最开始的 Redis Cluster 架构介绍的时候就已经解释了原因了:
1.Redis Cluster 没有使用一致性 Hash,而是采用 slot(槽)的概念,一共分成 16384 个槽。在构建集群时,这些槽会不重复地平均被分配到集群中的主节点。
2.在读写数据时,Redis Cluster 会先计算该数据的键值的 slot 号,然后再把读写请求分配到该slot 号所属的 Redis 节点。
3.而当不以集群方式连接到集群节点时,在计算 slot 号后,读写请求的分配工作却无法执行,就会出现上述报错。

1.4集群的关闭/重启
Redis Cluster 的官方文档并没有提供整个集群的关闭与重启的方法。推测可能是由于集群所有节点同时挂掉的可能性不高,毕竟即使偶尔集群中某个节点挂掉了,待其重启后又会自动重新加入集群,并不会带来太大影响。但是可能性不高并不代表不会发生,如何关闭/重启整个集群的方式还是需要知道的。

集群的关闭,执行这个命令即可(如果是部署到多台 IP 机器,需要登陆到每一台机器执行):

#pkill redis       # 直接杀掉集群的全部节点进程就可以了

集群的重启,只需要重新启动原集群中每一个节点就可以了,集群会自动恢复到关闭之前的状态(前提是不能删除 node-.conf、.aof、*.rdb 文件,否则会造成集群数据丢失)。

至此 Redis Cluster 集群模式部署完成。

1.5集群模式一键启动/停止/重启脚本
此脚本是以本实验部署方式和位置为基础编写的,仅适用于 Redis 节点均在同一台机器的场景。若要在其他地方使用,需要根据实际情况修改。

脚本内容如下:

#!/bin/bash
# 根据主从/哨兵模式的部署目录对应修改
export cluster_path=/usr/local/redis-cluster

# 启动函数
start()
{
${cluster_path}/redis-6390/src/redis-server ${cluster_path}/redis-6390/redis.conf
${cluster_path}/redis-6391/src/redis-server ${cluster_path}/redis-6391/redis.conf
${cluster_path}/redis-6392/src/redis-server ${cluster_path}/redis-6392/redis.conf
${cluster_path}/redis-6393/src/redis-server ${cluster_path}/redis-6393/redis.conf
${cluster_path}/redis-6394/src/redis-server ${cluster_path}/redis-6394/redis.conf
${cluster_path}/redis-6395/src/redis-server ${cluster_path}/redis-6395/redis.conf
echo "all running"
}

# 停止函数
stop()
{
ps -ef | grep "redis" | grep -v "grep" |awk '{print $2}'| while read pid
do
C_PID=$(ps --no-heading $pid | wc -l)
if [[ $C_PID == "1" ]]; then
kill -9 $pid
echo "PID=$pid is dead"
else
echo "PID=$pid not exists"
fi
done
echo "all dead"
}

# 脚本入口参数 start|stop|restart
case "$1" in
start)
start
;;
stop)
stop
;;
restart)
stop
start
;;
*)
printf 'Usage: %s {start|stop|restart}\n'"$prog"
exit 1
;;
esac

这是 shell 脚本,保存为 redis-cluster.sh(文件名任意)即可执行(若无法执行可能是换行符问题,可尝试通过 dos2unix 命令修正换行符)。

使用方式如下:

sh redis-cluster.sh start      # 启动
sh redis-cluster.sh stop       # 停止
sh redis-cluster.sh restart    # 重启
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章