如何搭建redis集群 --- redis-cluster

2020-08-11 15:32:22 浏览数 (1)

在之前的《来聊聊NoSql》一文中,已经说了redis三种集群模式中的主从哨兵,接下来再看看redis-cluster怎么玩。本文基于redis-cluster官方文档(https://redis.io/topics/cluster-tutorial),将其细化具体化,每一个操作过程都详细记录,如果官方文档读起来有点吃力,本文会是你不错的选择。 本文内容包括:

  • redis-cluster介绍
  • redis-cluster搭建的详细步骤
  • redis-cluster增删node

一、redis-cluster介绍

1、有了哨兵为什么还要搞redis-cluster? 之前说的哨兵,一般是一主二从,写数据都在master,读数据在slave,当master挂了之后会从两个slave中自动选出一个作为新的master。看似很完美,但问题就在slave是read only的,只能读数据,写数据的操作全都在master进行,对于写操作来说,redis相当于还是一个单机版的。如果对写操作并发很高,那可能就撑不住了,因此redis-cluster诞生。

2、redis-cluster机制: redis-cluster引入了一个哈希槽(slot)的概念,让数据与哈希槽关联,哈希槽再与节点关联。这样做的好处就是方便集群节点的增加或删除。假如没有哈希槽,数据直接和节点关联,也就是key直接和节点关联,如果要删除一个节点,那这个节点上的所有key都需要一个个地移走,比较麻烦;有了哈希槽,就可以将该节点上的哈希槽全部移走,比较方便。就好比有一些豆子,没有哈希槽需要一个个地捡起来,有哈希槽就是有个碗装着这些豆子,直接把碗拿起来就好了。redis-cluster中固定有16384个哈希槽,假如redis-cluster中有6个redis实例,3个master,每个master带1个(没有固定是1个,可以是N个)slave,比如3个master是A、B、C,它们的slave分别为A1、B1、C1,整个集群对外表现为一个整体。当你执行set k1 v1的时候,到底是set到哪台master上去了呢?还是3台master都有?其实是每个master都会分配到一部分哈希槽,比如:

  • A:0 ~ 5500
  • B:5501 ~ 11000
  • C:11001 ~ 16384

set的时候,对key经过一番计算:slot = CRC16(key) % 16384,就可以拿到哈希槽slot,就可以判断该key要set到哪个master上。

3、slave的作用是什么? 当有数据落在A上的时候,给客户端返回成功,并且异步将数据复制到A1上。如果A挂了,那么A1就会成为master继续提供服务。但如果A和A1都挂了,那么整个集群都将不能正常提供服务,所以每个master可以多整几个slave,保证高可用。而且正因为从master复制数据到slave是异步的,所以就有可能master给客户端返回成功了,还没来得及将数据复制到slave的时候,master挂了,然后slave变成了master,而这个master上是没有刚才写的数据的,这就出现了写丢失的情况。如果需要保证强一致性,就要用同步写的方式,但这将会牺牲性能。

二、redis-cluster的搭建

我这里用的是redis5.0,5.0以下的版本创建集群略有不同。 1、安装redis: 首先确保系统已经安装了gcc环境,然后在opt目录下新建redis-cluster目录,下载redis5.0的包到这个目录,再然后tar -zxvf redis5.0.xx.tar.gz解压redis,将解压出来的目录重命名为redis-server,再进入redis-server目录执行make,最后进入src目录下执行./redis-server看看能否成功启动redis。

2、准备工作:

  • /opt/redis-cluster目录下新建redis-conf目录;
  • 在上一步的redis-conf目录下新建700x目录,我这里新建的是从7001到7006六个目录;
  • 然后在700x目录下放一份redis.conf

3、修改redis.conf: 700x下的redis.conf文件要修改的地方如下:

代码语言:javascript复制
# 原本是bind 127.0.0.1,改成如下或者直接注释掉
bind 0.0.0.0
# 改端口,与目录名对应
port 700x
# 设置后台启动
daemonize yes
# 设置进程文件名
pidfile /var/run/redis_700x.pid
# 设置日志目录和日志文件名
logfile /opt/redis-cluster/redis-conf/700x/node.log
# 开启集群模式
cluster-enabled yes
# 集群配置文件的位置,这个启动集群后会自动生成,这里是设置它生成的目录和文件名
cluster-config-file /opt/redis-cluster/redis-conf/700x/nodes.conf
# 集群节点超时时间(毫秒)
cluster-node-timeout 15000

4、启动集群: 依次用上面修改的6份redis.conf启动6个redis实例,然后执行如下命令:

代码语言:javascript复制
/opt/redis-cluster/redis-server/src/redis-cli --cluster create 192.168.x.xx:7001 192.168.x.xx:7002 192.168.x.xx:7003 192.168.x.xx:7004 192.168.x.xx:7005 192.168.x.xx:7006 --cluster-replicas 1

--cluster-replicas 1表示为每个主机创建一个从机。执行该命令后,如果出现下面的提示信息,集群就启动成功了。

代码语言:javascript复制
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.

5、测试集群可用性: 进入src目录下执行./redis-cli -c -p 700x,任意连接一台,然后执行cluster nodes,就可以看到集群情况了。比如我的执行后返回如下信息:

代码语言:javascript复制
9f23a60b15fa1f0fab2e6393a84c4b78b3852fb6 192.168.2.43:7004@17004 slave fe163465d4c8b8a8a30d008d586a4ca56ed6d6db 0 1596642459801 4 connected
515f77f206d539cf3789cfa81bc678a3ea3697a5 192.168.2.43:7006@17006 slave 3ce363e5870562651ccf602f9b9a7a9b121c85a9 0 1596642458000 6 connected
fbcb70deb8917b3d84bb5d524657a5b674c43f0a 192.168.2.43:7005@17005 slave 2be934ea0e232d9a98796578a4a9e813fa4dda80 0 1596642457787 5 connected
2be934ea0e232d9a98796578a4a9e813fa4dda80 192.168.2.43:7002@17002 master - 0 1596642458794 2 connected 5461-10922
3ce363e5870562651ccf602f9b9a7a9b121c85a9 192.168.2.43:7003@17003 master - 0 1596642459000 3 connected 10923-16383
fe163465d4c8b8a8a30d008d586a4ca56ed6d6db 192.168.2.43:7001@17001 myself,master - 0 1596642457000 1 connected 0-5460

从返回的信息中可以知道,7001的slave是7004,7002的slave是7005,7003的slave是7006。然后执行set k1 v1,会返回如下信息:

代码语言:javascript复制
127.0.0.1:7001> set k1 v1
-> Redirected to slot [12706] located at 192.168.2.43:7003
OK
192.168.2.43:7003> 

它会对k1进行计算,最后根据计算结果发现它应该落在7003主机所拥有的哈希槽中,就会自动转到7003上。多设置几个值,就会发现它会自动的分配到不同的主机上,get取值的时候也会自动切换到对应的主机上。

6、集群中的某一台主机挂了咋整? ps -ef | grep redis | grep -v grep查看进程号,然后将7001端口的redis干掉,再去src目录下执行./redis-cli -c -p 700x连接redis,最后cluster nodes查看集群情况,返回如下信息:

代码语言:javascript复制
fbcb70deb8917b3d84bb5d524657a5b674c43f0a 192.168.2.43:7005@17005 slave 2be934ea0e232d9a98796578a4a9e813fa4dda80 0 1596643307980 5 connected
515f77f206d539cf3789cfa81bc678a3ea3697a5 192.168.2.43:7006@17006 slave 3ce363e5870562651ccf602f9b9a7a9b121c85a9 0 1596643307000 6 connected
9f23a60b15fa1f0fab2e6393a84c4b78b3852fb6 192.168.2.43:7004@17004 master - 0 1596643306969 7 connected 0-5460
3ce363e5870562651ccf602f9b9a7a9b121c85a9 192.168.2.43:7003@17003 master - 0 1596643306000 3 connected 10923-16383
2be934ea0e232d9a98796578a4a9e813fa4dda80 192.168.2.43:7002@17002 myself,master - 0 1596643305000 2 connected 5461-10922
fe163465d4c8b8a8a30d008d586a4ca56ed6d6db 192.168.2.43:7001@17001 master,fail - 1596643272364 1596643269000 1 disconnected

发现7001的状态是disconnected,并且7001的从机7004已经变成master了,备胎转正了。这个时候在随便set一些值,发现集群还是正常工作的。

如果再把7004也干掉,即7001和7004这个组合都宕机了,这个时候集群就不能正常提供服务了。再去操作就会报错(error) CLUSTERDOWN The cluster is down

如果7001挂了,7004成为master后,我再复活7001,那么7001是master还是slave还是与这个集群无关呢?答案是7001成了7004的slave,7004这个备胎永久转正了。

7、redis自带的集群启动脚本: 其实在redis的utils/create-cluster/目录下,有个create-cluster脚本,我们可以依次执行

代码语言:javascript复制
./create-cluster start
./create-cluster create

来创建集群,前面那些创建目录改配置文件那些操作都不用做了,直接执行这两条命令就可以启动一个集群,端口从30001开始,到30006。要停止集群执行如下命令即可。

代码语言:javascript复制
./create-cluster stop

8、编写shell脚本启动redis-cluster: 上面启动集群的方法虽然简单,但生产上一般还是会自己配置,可以自定义端口、日志文件位置等,方便维护。按照自己配置启动集群时,我们要依次启动6个redis实例,然后再启动集群,总共有7条命令要执行,其实可以编写一个shell脚本,一键启动。启动脚本内容如下:

代码语言:javascript复制
echo "starting redis-cluster"
oldNodesNum=`ps -ef | grep redis | grep -v grep | wc -l`
if [ "${oldNodesNum}" -ne 0 ]
then
  `ps aux | grep redis | grep cluster | grep -v grep | awk '{print $2}' | xargs kill -9`
fi
/opt/redis-cluster/redis-server/src/redis-server /opt/redis-cluster/redis-conf/7001/redis.conf
/opt/redis-cluster/redis-server/src/redis-server /opt/redis-cluster/redis-conf/7002/redis.conf
/opt/redis-cluster/redis-server/src/redis-server /opt/redis-cluster/redis-conf/7003/redis.conf
/opt/redis-cluster/redis-server/src/redis-server /opt/redis-cluster/redis-conf/7004/redis.conf
/opt/redis-cluster/redis-server/src/redis-server /opt/redis-cluster/redis-conf/7005/redis.conf
/opt/redis-cluster/redis-server/src/redis-server /opt/redis-cluster/redis-conf/7006/redis.conf
/opt/redis-cluster/redis-server/src/redis-cli --cluster create 192.168.2.65:7001 192.168.2.65:7002 192.168.2.65:7003 192.168.2.65:7004 192.168.2.65:7005 192.168.2.65:7006 --cluster-replicas 1
nodesNum=`ps -ef | grep redis | grep -v grep | wc -l`
if [ "${nodesNum}" -lt 6 ] 
then
   `ps aux | grep redis | grep cluster | grep -v grep | awk '{print $2}' | xargs kill -9` 
   echo "started failed"
else
   echo "started success"
fi

停止集群脚本如下:

代码语言:javascript复制
ps aux | grep redis | grep cluster | grep -v grep | awk '{print $2}' | xargs kill -9
activeNodesNum=`ps -ef | grep redis | grep cluster | grep -v grep | wc -l`
if [ "${activeNodesNum}" -ne 0 ]
then
   echo "shutdown failed"
else
   echo "shutdown success"
fi

三、redis-cluster增删node

假如现在master 7001(有key “k2”)和它的slave 7004挂掉了不能用了,想把它们俩从集群中移除,并且又启动了7007和7008两个实例准备加到集群中去,该怎么办?

0 人点赞