对于Redis集群方案有好多种,基本常用的就是twemproxy,codis,redis cluster这三种解决方案。
本博文介绍redis cluster。
上篇博文实现redis的高可用,针对的主要是master宕机的情况,我们发现所有节点的数据都是一样的,那么一旦数据量过大,redis也会存在效率下降的问题,redis3.0版本正式推出后,有效地解决了redis分布式方面的需求,当遇到单机内存,并发,流量等瓶颈时,可以采用Cluster架构方法达到负载均衡的目的。
1.redis使用中遇到的瓶颈
我们日常在对于redis的使用中,经常会遇到一些问题
1、容量问题,单实例redis内存无法无限扩充,达到32G后就进入了64位世界,性能下降。
2、并发性能问题,redis号称单实例10万并发,但也是有尽头的。
2.redis-cluster的优势
1、官方推荐,毋庸置疑。
2、去中心化,集群最大可增加1000个节点,性能随节点增加而线性扩展。
3、管理方便,后续可自行增加或摘除节点,移动分槽等等。
4、简单,易上手。
3.redis-cluster名词介绍
1、master 主节点、
2、slave 从节点
3、slot 槽,一共有16384数据分槽,分布在集群的所有主节点中。
4.redis-cluster的设计
Redis集群搭建的方式有多种,例如使用zookeeper等,但从redis 3.0之后版本支持redis-cluster集群,Redis-Cluster采用无中心结构,每个节点保存数据和整个集群状态,每个节点都和其他所有 节点连接。其redis-cluster架构图如下:
图中描述的是六个redis实例构成的集群
6379端口为客户端通讯端口
16379端口为集群总线端口
集群内部划分为16384个数据分槽,分布在三个主redis中。
从redis中没有分槽,不会参与集群投票,也不会帮忙加快读取数据,仅仅作为主机的备份。
三个主节点中平均分布着16384数据分槽的三分之一,每个节点中不会存有有重复数据,仅仅有自己的从机帮忙冗余。
其结构特点:
1、所有的redis节点彼此互联(PING-PONG机制),内部使用二进制协议优化传输速度和带宽。
2、节点的fail是通过集群中超过半数的节点检测失效时才生效。
3、客户端与redis节点直连,不需要中间proxy层。客户端不需要连接集群所有节点,连接集群中任何一个可用节点即可。
4、redis-cluster把所有的物理节点映射到[0-16383]slot上,cluster 负责维护node<->slot<->value。5、Redis集群预分好16384个桶,当需要在 Redis 集群中放置一个 key-value 时,根据 CRC16(key) mod 16384的值,决定将一个key放到哪个桶中。
5.redis cluster节点分配
假设我们现在有三个主节点,分别是:A, B, C 三个节点,它们可以是一台机器上的三个端口,也可以是三台不同的服务器。那么,采用哈希槽 (hash slot)的方式来分配16384个slot 的话,它们三个节点分别承担的slot 区间是:
节点A覆盖0-5460;
节点B覆盖5461-10922;
节点C覆盖10923-16383。
获取数据:
如果存入一个值,按照redis cluster哈希槽的算法: CRC16('key')%16384 = 6782。 那么就会把这个key 的存储分配到 B 上了。同样,当我连接(A,B,C)任何一个节点想获取'key'这个key时,也会这样的算法,然后内部跳转到B节点上获取数据。
新增一个主节点:
新增一个节点D,redis cluster的这种做法是从各个节点的前面各拿取一部分slot到D上,我会在接下来的实践中实验。大致就会变成这样:
节点A覆盖1365-5460
节点B覆盖6827-10922
节点C覆盖12288-16383
节点D覆盖0-1364,5461-6826,10923-12287
同样删除一个节点也是类似,移动完成后就可以删除这个节点了。
6.Redis Cluster主从模式
redis cluster 为了保证数据的高可用性,加入了主从模式,一个主节点对应一个或多个从节点,主节点提供数据存取,从节点则是从主节点拉取数据备份,当这个主节点挂掉后,就会有这个从节点选取一个来充当主节点,从而保证集群不会挂掉。
上面那个例子里,集群有ABC三个主节点,,如果这3个节点都没有加入从节点,如果B挂掉了,我们就无法访问整个集群了。A和C的slot也无法访问。
所以我们在集群建立的时候,一定要为每个主节点都添加了从节点, 比如像这样,,集群包含主节点A、B、C,,以及从节点A1、B1、C1,那么即使B挂掉系统也可以继续正确工作。
B1节点替代了B节点,所以Redis集群将会选择B1节点作为新的主节点,集群将会继续正确地提供服务。 当B重新开启后,它就会变成B1的从节点。
不过需要注意,如果节点B和B1同时挂了,Redis集群就无法继续正确地提供服务了。
1.selinux和firewalld状态为disabled
2.各主机信息如下:
主机 | ip |
---|---|
server1 | 172.25.83.1 |
3.节点准备(官方推荐三主三从的配置方式。)
redis3.0及以上版本实现,集群中至少应该有奇数个节点,所以至少有三个节点,每个节点至少有一个备份节点,所以下面使用6节点(主节点、备份节点由redis-cluster集群确定)。
172.25.83.1:7001,172.25.83.1:7002,172.25.83.1:7003,172.25.83.1:7004,172.25.83.1:7005,172.25.83.1:7006搭建初始集群。
172.25.83.1:7007,172.25.83.1:7008扩容时用到。
配置server1:(因为server1之前是做过主从切换的,所以redis已经安装好。)
1.停掉redis服务(redis服务本身监听的端口是6379端口)
[root@server1 ~]# /etc/init.d/redis_6379 stop
2.创建目录,用于存放redis Cluster对应的节点配置文件
[root@server1 ~]# mkdir /usr/local/rediscluster
[root@server1 ~]# cd /usr/local/rediscluster/
[root@server1 rediscluster]# mkdir 700{1..6}
[root@server1 rediscluster]# ls
7001 7002 7003 7004 7005 7006
3.编写redis Cluster对应的节点的配置文件,并启动redis Cluster对应的所有节点
(1)编辑配置文件,下面给出的是一个节点7001的配置文件(其余节点(7002-7006)的配置文件跟这个配置文件类似,只需要将其中的7001改为对应的节点即可)
[root@server1 rediscluster]# cd 7001/
[root@server1 7001]# vim redis.conf
port 7001 #端口
cluster-enabled yes #如果是yes,表示启用集群,否则以单例模式启动
cluster-config-file nodes.conf #请注意,尽管有此选项的名称,但这不是用户可编辑的配置文件,而是Redis群集节点每次发生更改时自动保留群集配置(基本上为状态)的文件,以便能够 在启动时重新读取它。 该文件列出了群集中其他节点,它们的状态,持久变量等等。 由于某些消息的接收,通常会将此文件重写并刷新到磁盘上。
cluster-node-timeout 5000 #Redis群集节点超过不可用的最长时间,而会将其视为失败。 如果主节点超过指定的时间不可达,它将由其从属设备进行故障切换。 此参数控制Redis群集中的其他重要事项。 值得注意的是,每个无法在指定时间内到达大多数主节点的节点将停止接受查询。
appendonly yes #是否开启appendonlylog,开启的话每次写操作会记一条log,这会提高数据抗风险能力,但影响效率。
pidfile "/usr/local/rediscluster/7001/redis.pid" #pid文件存放的位置
logfile "/usr/local/rediscluster/7001/redis.log" #日志文件存放的位置
daemonize yes #打入后台
dir "/usr/local/rediscluster/7001" #该节点所在目录
(2)启动节点,下面给出的是7001节点的启动方法(其余节点(7002-7006)的启动方法,类似。只需要把7001改为对应的节点即可)
[root@server1 7001]# redis-server redis.conf #启动7001节点
[root@server1 7001]# netstat -antulpe #查看端口,是否有7001端口,以确保7001端口已经启动
4.创建集群(集群中的主从节点是随机的)
redis3版本利用redis-trib.rb命令来创建集群,如下所示:
节点全部启动后,每个节点目前只能识别出自己的节点信息,彼此之间并不知道对方的存在;
采用redis-trib.rb来实现集群的快速搭建,redis-trib.rb是采用Rudy实现的集群管理工具,内部通过Cluster相关命令帮我们实现简化集群创建,检查,槽迁移和均衡等常见的运维操作。
(1)安装redis-trib所需的 ruby环境
[root@server1 ~]# yum install ruby -y
(2)将redis-trib.rb对应的脚本文件放到/usr/local/bin目录下,以便直接敲击redis-trib.rb命令
[root@server1 ~]# cd redis-5.0.3/src/
[root@server1 src]# cp redis-trib.rb /usr/local/bin/
(3)使用redis-trib.rb创建集群
既然redis-trib.rb命令用不了,那么/usr/local/bin目录下的redis-trib.rb脚本就可以删除了。
但是redis5版本,不再使用redis-trib.rb来命令来创建集群了,从上面的提示,我们可以看出,需要使用redis-cluster来创建集群,过程如下:
[root@server1 ~]# redis-cli --cluster help #在使用redis-cli --cluster命令之前,可以先查看下redis-cli --cluster命令的帮助
create host1:port1 ... hostN:portN
--cluster-replicas
[root@server1 ~]# redis-cli --cluster create --cluster-replicas 1 127.0.0.1:7001 127.0.0.1:7002 127.0.0.1:7003 127.0.0.1:7004 127.0.0.1:7005 127.0.0.1:7006 #输入yes
# 使用create命令 --replicas 1 参数表示为每个主节点创建一个从节点,其他参数是实例的地址集合。
创建过程中会给出主从节点角色分配的计划,如下图所示
为什么172.25.83.1:7001,172.25.83.1:7002,172.25.83.1:7003是主节点,请看上图,当我们同意这份计划之后,输入yes,redis-cli --cluster开始执行节点握手和槽分配操作,输出如下:
最终可以看到:(当然下面的信息,也可以登录每个节点(例如:redis-cli -p 7001),查看每个节点的info replication,来获取)
(1)主节点:7001;对应的从节点为:7005
(2)主节点:7002;对应的从节点为:7006
(3)主节点:7003;对应的从节点为:7004
此时可以利用下面的命令来查看该集群的信息以查看该集群的主节点:
[root@server1 bin]# redis-cli --cluster info 127.0.0.1:7001 #节点7001,可以换为任何一个节点,看到的结果都是一样的
redis集群的测试:测试存取值
客户端连接集群redis-cli需要带上 -c ,redis-cli -c -p 端口号
根据redis-cluster的key值分配,name应该分配到节点7002[5461-10922]上,上面显示redis cluster自动从7001跳转到了7002节点。
我们可以测试一下7004从节点获取name值
结论:
7006位7003的从节点,从上面也是自动跳转至7002获取值,这也是redis cluster的特点,它是去中心化,每个节点都是对等的,连接哪个节点都可以获取和设置数据。
(1)查看7002节点上的所有key,并手动将7002节点挂掉
此时看到进程中已经没有7002节点对应的进程了
(2)再次查看集群的信息,可以看到7006节点(之前7002节点的从节点)已经成为主节点。查看7006节点的所有key,可以看出7006节点承担着7002节点的角色。对外提供的服务不受影响
但是此时,如果将7006节点也手动down掉,那么该集群也就费了。
配置过程
新增节点:172.25.83.1:7007,172.25.83.1:7008。配置文件与之前的基本一致
1.创建目录
[root@server1 rediscluster]# mkdir 700{7,8}
[root@server1 rediscluster]# ls
7001 7002 7003 7004 7005 7006 7007 7008
2.编辑配置文件
[root@server1 7007]# vim redis.conf
[root@server1 7008]# vim redis.conf
3.启动7007和7008节点
[root@server1 7007]# redis-server redis.conf
[root@server1 7008]# redis-server redis.conf
[root@server1 7008]# ps ax
4.加入集群
(1)新增主节点
[root@server1 7008]# redis-cli --cluster help
add-node new_host:new_port existing_host:existing_port
--cluster-slave
--cluster-master-id
#existing_port——这个是必传参数,用来从一个节点获取整个集群信息,相当于获取集群信息的入口。
check host:port
--cluster-search-multiple-owners
#host:port——这个是必传参数,用来从一个节点获取整个集群信息,相当于获取集群信息的入口。
[root@server1 7008]# redis-cli --cluster add-node 127.0.0.1:7007 127.0.0.1:7001
#往集群中添加端口7007,
#存在的端口可以写7001-7006中的任意一个
[root@server1 7008]# redis-cli --cluster check 127.0.0.1:7001
#检测节点,以查看7007节点的id号,以便将7008加入集群,作为7007的从节点。
#端口可以写7001-7007中的任意一个
(2)新增从节点
[root@server1 7008]# redis-cli --cluster add-node 127.0.0.1:7008 127.0.0.1:7007 --cluster-slave --cluster-master-id "239b6e3dfa0d35fb64ed7b3c839ec99129107a90"
#将7008节点添加成7007的从节点
#这里的239b6e3dfa0d35fb64ed7b3c839ec99129107a90为节点7007的id号
#这里的第二个节点,必须写7007,不能写别的节点。因为这步是要将节点7008添加称为7007的节点
[root@server1 7008]# redis-cli --cluster check 127.0.0.1:7001
#检测节点,以查看7007节点和7008节点是否添加成功,并查看槽的分配情况
至此,集群的添加也就完成了。
5.迁移槽和数据
[root@server1 7008]# redis-cli --cluster help
reshard host:port
--cluster-from
--cluster-to
--cluster-slots
--cluster-yes
--cluster-timeout
--cluster-pipeline
--cluster-replace
#host:port——这个是必传参数,用来从一个节点获取整个集群信息,相当于获取集群信息的入口。
[root@server1 7008]# redis-cli --cluster reshard 127.0.0.1:7007 --cluster-from all --cluster-to "239b6e3dfa0d35fb64ed7b3c839ec99129107a90" --cluster-slots 300 --cluster-yes
#从所有节点迁移300个槽给127.0.0.1:7007,这里的"239b6e3dfa0d35fb64ed7b3c839ec99129107a90"为节点7007的id号;
#这里的节点7007可以写7001-7008中的任意一个节点任意的一个节点,因为帮助中没有表明是已经存在的节点,还是新节点,所以都可以。
#这里也可以写4096个槽,如果这里写4096(13684/4=4096),那么槽就得到了平均分配,也就不需要下一步的平均分配槽的操作了。
6.执行命令使得槽平均分配
[root@server1 7008]# redis-cli --cluster help
rebalance host:port
--cluster-weight
--cluster-use-empty-masters
--cluster-timeout
--cluster-simulate
--cluster-pipeline
--cluster-threshold
--cluster-replace
host:port:这个是必传参数,用来从一个节点获取整个集群信息,相当于获取集群信息的入口。
--weight :节点的权重,格式为node_id=weight,如果需要为多个节点分配权重的话,需要添加多个--weight 参数,即--weight b31e3a2e=5 --weight 60b8e3a1=5,node_id可为节点名称的前缀,只要保证前缀位数能唯一区分该节点即可。没有传递–weight的节点的权重默认为1。
--auto-weights:这个参数在rebalance流程中并未用到。
--threshold :只有节点需要迁移的slot阈值超过threshold,才会执行rebalance操作。具体计算方法可以参考下面的rebalance命令流程的第四步。
--use-empty-masters:rebalance是否考虑没有节点的master,默认没有分配slot节点的master是不参与rebalance的,设置--use-empty-masters可以让没有分配slot的节点参与rebalance。
--timeout :设置migrate命令的超时时间。
--simulate:设置该参数,可以模拟rebalance操作,提示用户会迁移哪些slots,而不会真正执行迁移操作。
--pipeline :与reshar的pipeline参数一样,定义cluster getkeysinslot命令一次取出的key数量,不传的话使用默认值为10。
[root@server1 7008]# redis-cli --cluster rebalance 127.0.0.1:7008 --cluster-threshold 1 --cluster-use-empty-masters #平衡集群节点slot数量
[root@server1 7008]# redis-cli --cluster check 127.0.0.1:7008 #检测节点,查看slot的分配情况,以查看是否平衡成功
看到每个主节点的solt数量都是4096(solt的区间为:0-1364;5461-6826;10923-12287),表示solt数量平衡成功。
测试过程:
测试一下7007主节点和7008从节点获取name值
结论:
节点7007和7008可以得到name的值,表示redis cluster集群的扩容配置成功。(值的注意的是:当7008节点获取name值时,自动跳转到了7007节点,而不是之前的7002节点。这是因为:之前redis cluster在扩容时。将存储name值的slot(5798)转移到7007主节点上了)
我们下线7007和7008节点
(1)通过集群节点信息,我们之道7007主节点负责的solt为:0-1364;5461-6826;10923-12287。现在我们把0-1364迁移到7001;5461-6826迁移到7002;10923-12287迁移到7003。
[root@server1 7008]# redis-cli --cluster reshard 127.0.0.1:7007
How many slots do you want to move (from 1 to 16384)? 1365 #1365个slot,第一次默认是0-1364
What is the receiving node ID? 14a0f59768d46879c929afc62018421db53a5efd #7001节点的id
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: 239b6e3dfa0d35fb64ed7b3c839ec99129107a90 #70007节点的id
Source node #2: done
Do you want to proceed with the proposed reshard plan (yes/no)? yes
[root@server1 7008]# redis-cli --cluster reshard 127.0.0.1:7007
How many slots do you want to move (from 1 to 16384)? 1366 #移1366个slot,第二次默认是5461-6826
What is the receiving node ID? d93240e95225c62eb00072b378d6956c1a1ac8c8 #7002节点的id
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: 239b6e3dfa0d35fb64ed7b3c839ec99129107a90 #7007节点的id
Source node #2: done
Do you want to proceed with the proposed reshard plan (yes/no)? yes
[root@server1 7008]# redis-cli --cluster reshard 127.0.0.1:7007
How many slots do you want to move (from 1 to 16384)? 1365 #1365个slot,第三次默认是10923-12287
What is the receiving node ID? 6444903d022f2707db089ba53b76ea9170df06b5 #7003的id
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: 239b6e3dfa0d35fb64ed7b3c839ec99129107a90 #7007的id
Source node #2: done
Do you want to proceed with the proposed reshard plan (yes/no)? yes
槽节点迁移完之后,查看集群的信息,发现7007已经没有分配槽了
(2)忘记节点:
由于集群内的节点不停地通过Gossip消息彼此交换节点信息,因此需要通过一种健壮的机制让集群内所有节点忘记下线的节点。也就是说让其他节点不再与要下线的节点进行Gossip消息交换。
利用redis-cli --cluster del-node命令实现节点下线,先下线从节点再下线主节点,避免不必要的全量复制。命令如下:
[root@server1 7008]# redis-cli --cluster help
del-node host:port node_id
[root@server1 7008]# redis-cli --cluster del-node 127.0.0.1:7008 7fe1e1e925a1a7219d014255278a1a3a9b2cab18
#下线从节点7008
#该id是7008的id,并且节点必须写7008。不能随便写个节点
[root@server1 7008]# redis-cli --cluster del-node 127.0.0.1:7007 239b6e3dfa0d35fb64ed7b3c839ec99129107a90
#下线主节点7007
#该id是7007的id,并且节点必须写7008。不能随便写个节点
此时查看集群的信息,发现7007和7008节点已经不存在,其进程也已被杀死
结论:
集群信息中已经没有节点7007和7008,表名redis cluster集群的收缩配置成功。