前言

redis主从集群,哨兵集群,可以应对高并发读的场景,但是高并发写与海量数据存储的问题依然不能解决,主从之间数据同步的缺陷就限制了主服务器内存不能设置的太高,为解决并发写与高可用的问题引入了分片集群(cluster)


一、什么是cluster集群?

Cluster集群模式也被称为切片集群是Redis官方推荐使用的一种模式,Cluster方案采用哈希槽(hash slot)来处理实例之间的映射关系,在集群中总共有16384个哈希槽,默认形式是将16384个哈希槽分配给所有的节点,即哈希槽与集群节点绑定。每个节点分配一段哈希槽类似数据分区,每个键值按照CRC16算法得到哈希值再将其对16384取模CRC16(key) modulo 16384通过最终的结果得到key值存在的哈希槽位置。集群中的节点分为主节点和从节点:只有主节点负责读写请求和集群信息的维护;从节点只进行主节点数据和状态信息的复制。Redis Cluster,是Redis 3.0开始引入的分布式存储方案。

二、Cluster集群概述

1.集群作用

集群的作用,可以归纳为两点:

  1. 数据分区: 数据分区(或称数据分片)是集群最核心的功能。集群将数据分散到多个节点,一方面突破了Redis单机内存大小的限制,存储容量大大增加;另一方面每个主节点都可以对外提供读服务和写服务,大大提高了集群的响应能力。Redis单机内存大小受限问题,在介绍持久化和主从复制时都有提及;例如,如果单机内存太大,bgsave和bgrewriteaof的fork操作可能导致主进程阻塞,主从环境下主机切换时可能导致从节点长时间无法提供服务,全量复制阶段主节点的复制缓冲区可能溢出……等
  2. 高可用: 集群支持主从复制和主节点的自动故障转移(与哨兵类似),当任一节点发生故障时,从节点可以代替主节点, 集群仍然可以对外提供服务。以及当某master节点故障时,其他master仍然可提供服务

2.集群特征

  1. 集群中有多个master,每个master保存不同的数据
  2. 每个master都可以有多个slave节点
  3. master之间通过ping,彼此间检测健康状态
  4. 客户端可以请求访问集群中任意节点,最终都会被转发到正确的节点上

3.应用场景

Redis集群模式适用于需要横向扩展和高可用性的场景。
当数据量大,单个Redis节点无法满足需求时,可以通过搭建Redis集群来提供更大的存储容量和处理能力。同时,Redis集群模式通过数据分片和数据复制实现高可用性,即使其中某个节点发生故障,整个集群仍然可用。


三、cluster集群搭建

本文是基于redis5.0版本搭建,本文在一台虚拟机中使用不同的端口号模拟不同的redis示例redis集群架构图如下:

ip:端口角色
127.0.0.1:6379master
127.0.0.1:6380master
127.0.0.1:6381master
127.0.0.1:6382slave
127.0.0.1:6383slave
127.0.0.1:6384slave

redis集群6节点

1. 创建运行实例目录

  1. 在redis的安装目录中创建文件夹以运行不同的实例,创建目录
    mkdir -p /data/soft/redis_5_0_0/{redis-6379,redis-6380,redis-6381,redis-6382,redis-6383,redis-6384}/{conf,run,logs,dbcache}
  1. redis-6379代表运行的实例
    目录介绍:
    conf : 实例配置文件目录
    dbcache: rdb数据目录
    logs : 运行日志目录
    run: 实例pid文件目录
# /data/soft/redis_5_0_0 该目录为redis的安装目录
[root@master redis_5_0_0] cd /data/soft/redis_5_0_0
[root@master redis_5_0_0]# 
[root@master redis_5_0_0]# ls
bin   redis-6379  redis-6380  redis-6381  redis-6382  redis-6383  redis-6384
[root@master redis-6379]# cd  redis-6379
[root@master redis-6379]# ls
conf  dbcache  logs  run

2.修改redis配置文件及分发

  1. 修改redis.conf 配置文件,如下:
    daemonize yes #开启后台运行
    requirepass 123456 #设置redis数据库密码
    masterauth 123456 #设置集群节点间访问密码
    protected-mode no
    port 6379
    pidfile /data/soft/redis_5_0_0/redis-6379/run/redis_6379.pid
    logfile “/data/soft/redis_5_0_0/redis-6379/logs/redis.log”
    dir /data/soft/redis_5_0_0/redis-6379/dbcache/
    #开启redis-cluster
    cluster-enabled yes
    #设置节点之间超时时间
    cluster-node-timeout 15000
    #指定cluster-config-file,不能与别的节点相同
    cluster-config-file nodes-6379.conf
  1. 将redis.conf分发复制到不同实例的conf文件夹中
# 分发redis.conf配置文件,在redis.conf配置文件路径中执行如下命令
[root@master redis_5_0_0] echo redis-6379/conf/redis-6379.conf  redis-6380/conf/redis-6380.conf  redis-6381/conf/redis-6381.conf  redis-6382/conf/redis-6382.conf  redis-6383/conf/redis-6383.conf  redis-6384/conf/redis-6384.conf  | xargs -t -n 1 cp redis.conf
  1. 将配置文件中含有端口配置改为对应的实例端口
    vim redis-6380.conf
    :1,$s/6379/6380/g # 命令模式下执行,将配置文件中的6379改为6380

3.分别启动redis实例

运行一下命令启动实例:
printf ‘%s\n’ redis-6379/conf/redis-6379.conf redis-6380/conf/redis-6380.conf redis-6381/conf/redis-6381.conf redis-6382/conf/redis-6382.conf redis-6383/conf/redis-6383.conf redis-6384/conf/redis-6384.conf | xargs -I{} -t /data/soft/redis_5_0_0/bin/redis-server {}

  • 只是启动6台redis服务器,服务器之间没有任何的关联
[root@master redis_5_0_0]# printf '%s\n' redis-6379/conf/redis-6379.conf  redis-6380/conf/redis-6380.conf  redis-6381/conf/redis-6381.conf  redis-6382/conf/redis-6382.conf  redis-6383/conf/redis-6383.conf  redis-6384/conf/redis-6384.conf | xargs -I{} -t /data/soft/redis_5_0_0/bin/redis-server {}
/data/soft/redis_5_0_0/bin/redis-server redis-6379/conf/redis-6379.conf 
/data/soft/redis_5_0_0/bin/redis-server redis-6380/conf/redis-6380.conf 
/data/soft/redis_5_0_0/bin/redis-server redis-6381/conf/redis-6381.conf 
/data/soft/redis_5_0_0/bin/redis-server redis-6382/conf/redis-6382.conf 
/data/soft/redis_5_0_0/bin/redis-server redis-6383/conf/redis-6383.conf 
/data/soft/redis_5_0_0/bin/redis-server redis-6384/conf/redis-6384.conf 
[root@master redis_5_0_0]# ps -axu | grep redis | grep -v grep
root       2134  0.3  0.6 163192 11804 ?        Ssl  22:49   0:00 /data/soft/redis_5_0_0/bin/redis-server 127.0.0.1:6379 [cluster]
root       2139  0.3  0.4 157048  7716 ?        Ssl  22:49   0:00 /data/soft/redis_5_0_0/bin/redis-server 127.0.0.1:6380 [cluster]
root       2144  0.0  0.4 157048  7720 ?        Ssl  22:49   0:00 /data/soft/redis_5_0_0/bin/redis-server 127.0.0.1:6381 [cluster]
root       2149  0.0  0.4 157048  7720 ?        Ssl  22:49   0:00 /data/soft/redis_5_0_0/bin/redis-server 127.0.0.1:6382 [cluster]
root       2154  0.0  0.4 157048  7720 ?        Ssl  22:49   0:00 /data/soft/redis_5_0_0/bin/redis-server 127.0.0.1:6383 [cluster]
root       2159  2.3  0.4 157048  7724 ?        Ssl  22:49   0:00 /data/soft/redis_5_0_0/bin/redis-server 127.0.0.1:6384 [cluster]

4.创建集群

Redis5.0之前集群命令都是用安装包下的src/redis-trib.rb 来实现的,因为redis-trib.rb是由Ruby实现的所以需要安装Ruby的运行环境, 本文中以Redis5.0.0为例来安装故不需要Ruby环境。可以直接在redis-cli 中直接使用集群相关的操作命令

  1. 命令解释:
    redis-cli --cluster : 代表集群操作命令
    create :代表是创建集群
    cluster-replicas 1: 指定集群中每个master的副本数为1,此时主节点master的总数就是 集群节点总数除以(replicas+1)等于N,因此节点中的前N个为master节点,其他节点都是slave节点,随机分配到不同的master
# 执行创建集群命令
[root@master redis_5_0_0]#   /data/soft/redis_5_0_0/bin/redis-cli --cluster create 127.0.0.1:6379 127.0.0.1:6380 127.0.0.1:6381 127.0.0.1:6382 127.0.0.1:6383 127.0.0.1:6384 --cluster-replicas 1 -a 123456

# 查看集群节点信息
# 6484是6380的从节点
# 6383是6379的从节点
# 6382是6381的从节点
[root@master redis_5_0_0]# redis-cli -p 6379 
127.0.0.1:6379> AUTH 123456
127.0.0.1:6379> CLUSTER NODES
cfeecfe2803b211051ab5944cdf5ee022b55dd0c 127.0.0.1:6384@16384 slave 3ed64d2aa3ee633c88f4bbc7d7f1233ce5991f73 0 1714230693190 6 connected
21ea85f10415e26d2fcc3b2920968d8f849a113c 127.0.0.1:6381@16381 master - 0 1714230693000 3 connected 10923-16383
3ed64d2aa3ee633c88f4bbc7d7f1233ce5991f73 127.0.0.1:6380@16380 master - 0 1714230693000 2 connected 5461-10922
b3ddc7b9ae0dea7dc4a280c225c252a3986163c2 127.0.0.1:6379@16379 myself,master - 0 1714230691000 1 connected 0-5460
73f62cf8f45b0432b3df37c5fa21e74bfe6ab179 127.0.0.1:6382@16382 slave 21ea85f10415e26d2fcc3b2920968d8f849a113c 0 1714230692181 4 connected
50492b7275b66373670cf600fab29709e8da886d 127.0.0.1:6383@16383 slave b3ddc7b9ae0dea7dc4a280c225c252a3986163c2 0 1714230691000 5 connected


集群节点信息

四、cluster实践

1. 查询判断slot插槽与实例的绑定

客户端登录redis,使用 CLUSTER NODES 可以查看主从节点,已经主节点分配的slot(哈希槽)

127.0.0.1:6379> CLUSTER NODES
	cfeecfe2803b211051ab5944cdf5ee022b55dd0c 127.0.0.1:6384@16384 slave 3ed64d2aa3ee633c88f4bbc7d7f1233ce5991f73 0 1714305487728 6 connected
	21ea85f10415e26d2fcc3b2920968d8f849a113c 127.0.0.1:6381@16381 master - 0 1714305487000 3 connected 10923-16383
	3ed64d2aa3ee633c88f4bbc7d7f1233ce5991f73 127.0.0.1:6380@16380 master - 0 1714305486000 2 connected 5461-10922
	b3ddc7b9ae0dea7dc4a280c225c252a3986163c2 127.0.0.1:6379@16379 myself,master - 0 1714305488000 1 connected 0-5460
	73f62cf8f45b0432b3df37c5fa21e74bfe6ab179 127.0.0.1:6382@16382 slave 21ea85f10415e26d2fcc3b2920968d8f849a113c 0 1714305489758 4 connected
	50492b7275b66373670cf600fab29709e8da886d 127.0.0.1:6383@16383 slave b3ddc7b9ae0dea7dc4a280c225c252a3986163c2 0 1714305488742 5 connected

上述集群节点信息中,每列的含义

  • 第一列表示节点ID,集群中唯一的

  • 第二列表示节点的ip与端口号

  • 第三列表示节点的状态与角色,常见的标识包括: myself:表示当前节点是执行命令的节点,sl master:表示节点是主节点,slave:表示节点是副本节点,fail?:表示节点可能处于故障状态

  • 第四列 如果是主节点,则用-占位,如果是副本节点,则显示该副本节点的主节点是哪个(主节点的ID)

  • 第五列用于检测节点之间的网络连接情况Ping/Pong响应的时间

  • 第六列表示13位时间戳

  • 第七列在集群中选择新的主节点的内部计数器

  • 第八列显示节点之间的连接状态 可以时connected(连接正常),disconnected(连接断开)

  • 第九列节点负责的哈希槽范围

    以此可以判断
    主节点 127.0.0.1:6379 从节点端口 6383 插槽范围 0-5460
    主节点127.0.0.1:6380 从节点端口 6384 插槽范围 5461-10922
    主节点127.0.0.1:6381 从节点端口 6382 插槽范围 10923-16383

2.如何判断某个key应该在哪个实例分布

使用 命令 CLUSTER KEYSLOT key 可以查看 key对应的slot(哈希槽)

	[root@master ~]# redis-cli -p  6380
	127.0.0.1:6380>
	127.0.0.1:6379> CLUSTER KEYSLOT good_info_id:123123534534
  (integer) 2834 
	127.0.0.1:6380> set good_info_id:123123534534 123123123
	(error) MOVED 2834 127.0.0.1:6379
	127.0.0.1:6380>

key: good_info_id:123123534534 根据crc16计算哈希值为2834,2834对16384取余得到slot值为2834,slot为2834对应的实例为127.0.0.1:6379,此时在客户端为127.0.0.1:6380运行出现MOVED的错误。

解决方式
1. 直接更换实例连接 redis-cli -p 6379
2. 在连接时使用 redis-cli时增加 -c参数
[root@master ~]# redis-cli -p 6380 -c
127.0.0.1:6380> AUTH 123456
OK
127.0.0.1:6380> set good_info_id:123123534534 123123123
-> Redirected to slot [2834] located at 127.0.0.1:6379
(error) NOAUTH Authentication required.
127.0.0.1:6379>

3.如何将同一类数据固定的保存在同一个实例

数据key不是与节点绑定,而是与插槽绑定,redis会根据key的有效部分计算插槽值,情况分两种

    1. key中包含 {},且{}中至少包含一个字符,那么{}中的部分为有效部分
    1. 二是key中不包含{},那么整个key都是有效部分

例如: key是good_info那么就根据good_info计算,如果是{shops:}good_info则根据shops:计算,
计算方式是利用CRC16算法得到一个hash值,然后对16384取余,得到的结果就是slot的值,取余计算后的值为4010,4010与端口号为6379的实例绑定

  127.0.0.1:6379> set {shops:}good_info  1231231
  OK
  127.0.0.1:6379> set {shops:}conf_info  1231231
  OK
  127.0.0.1:6379> keys *
  1) "{shops:}conf_info"
  2) "{shops:}good_info"`

3.如何将一个节点添加到集群

在集群中如何增加一个节点?以及如何将同一实例下的{shops:}good_info,迁移到新节点?
将新增的redis实例127.0.0.1:6385 的节点添加到新集群中,新增节点redis配置与搭建集群配置类似,在此就不再赘述。新增节点主要有两个操作: 一将新增节点加入集群, 二 将集群中的部分 Slot 迁移至新增的节点,如有固定业务key需迁移,还需确定固定业务key的槽点主要操作,受先查看一下命令帮助文档

  • 查看命令帮助 /data/soft/redis_5_0_0/bin/redis-cli --cluster help查看Redis集群相关命令帮助文档
  • new_host:new_port:为要新添加的主节点 IP 和端口
  • existing_host:existing_port:表示的是环境中已存在的最后一个主节点的 IP 和端口,这个可以通过 查看节点信息得知,根据 slots 槽数,127.0.0.1:6381 对应的节点槽数是 10923-16383,16383表示的是最后的槽数
  • –cluster-master-id:表示的是最后一个主节点的节点 ID,表示的是新添加的节点是这个节点的从节点
  • –cluster-slave: 表示添加的节点为副本节点(slave)
    无参数-–cluster-master-id, --cluster-slave时新加的节点默认是主节点

迁移过程如下:

  1. 使用如下命令将新节点加入新集群中 /data/soft/redis_5_0_0/bin/redis-cli -a 123456 --cluster add-node 127.0.0.1:6385 127.0.0.1:6381
  2. 查看key的哈希槽位置 命令 CLUSTER KEYSLOT key。 {shops:}good_info 对应的哈希槽为4010
  3. 将集群中的部分 Slot 迁移至新增的节点,使用如下命令:redis-cli -a 123456 --cluster reshard 127.0.0.1:6385

注意 :6379的节点范围为0-5460只要将4010槽点范围迁移至新节点即可,其他源节点必须是主节点。

#查看新节点无任何数据
127.0.0.1:6385> keys *
(empty list or set)
127.0.0.1:6385> 
# 查看key对应的slot槽点
[root@master bin]# ./redis-cli -p 6379
127.0.0.1:6379> AUTH 123456
127.0.0.1:6379> CLUSTER KEYSLOT {shops:}good_info
(integer) 4010

# 添加完成后,查看新节点数据
127.0.0.1:6379> get {shops:}good_info
-> Redirected to slot [4010] located at 127.0.0.1:6385
(error) NOAUTH Authentication required.
127.0.0.1:6385> AUTH 123456
OK
127.0.0.1:6385> get {shops:}good_info
"1231231"
127.0.0.1:6385> 

127.0.0.1:6380> set {shops:}good_info  445566
-> Redirected to slot [4010] located at 127.0.0.1:6385
OK
127.0.0.1:6385> keys *
1) "{shops:}conf_info"
2) "{shops:}good_info"
127.0.0.1:6385> 

下图为添加节点后集群所有节点
新增的节点
执行添加节点命令后,执行过程如下
添加节点的过程总结: 在集群增加节点过程中可以发现,数据与slot绑定,而不是与节点直接绑定,可以更方便的实现数据在不同节点之间的迁移维护。

4.故障转移

当集群中的一个master节点宕机时会发生什么呢?
现在使用linux中watch命令实时查看集群信息变化,使用 如下命令:watch /data/soft/redis_5_0_0/bin/redis-cli -p 6380 -a 123456 cluster nodes来观察集群节点6379出现故障时集群变化。

集群节点状态变化之前
集群节点状态变化之前
关闭6379的节点后,6379状态为fail?,可能出现故障
6379疑似故障等待几秒后6379状态为fail,集群判断出现故障,将6379的slave从节点6383提升为master
6383提升为主节点
启动6379节点后,6379又变为slave从节点在这里插入图片描述
总结: 整个变化过程中首先是6379实例与其他实例失去连接,然后是疑似宕机,最后确定下线,自动提升一个slave节点为新的master

5.数据迁移

在实际应用场景中,有时会对主节点所在的机器进行各方面的维护等,需要对主节进行下线,也就是手动故障转移
案例:在故障转移中,6379已成为slave,现将6379 重新提升为master,使6383成为slave

在要提升为主节点的slave节点中执行如下命令: CLUSTER FAILOVER
注意:使谁成为主节点master,就在主节点master对应的从节点上执行 CLUSTER FAILOVER命令

执行命令如下:

[root@master redis_5_0_0]# /data/soft/redis_5_0_0/bin/redis-cli -c -p 6379 -a 123456 
127.0.0.1:6379> CLUSTER FAILOVER 

执行CLUSTER FAILOVER 后,集群节点变化如下:在这里插入图片描述手动执行failover,支持三种不同的参数,

  • 缺省值,严格按照保证数据完整性流程执行
  • force 强制执行省略offset一致性性校验
  • takeover 直接执行第5步忽略一致性,忽略master的状态,和其他master的意见

failover 执行时序图在这里插入图片描述

五、cluster集群的优缺点

1.cluster集群优点

  1. 高可用多主多从:服务故障的情况,影响范围小,Redis集群有多个master,可以减小访问瞬断问题的影响,若集群中有一个master挂了,正好需要向这个master写数据,这个操作需要等待但向其他master节点写数据不受影响
  2. 高并发:Redis集群有多个master,可以提供更高的并发量,包括写与读
  3. 数据分布,存储数据量大:Redis集群可以分片存储,这样就可以存储更多的数据
  4. 负载均衡:客户端可以直接连接 Redis Cluster 的任何节点,数据会自动分配到正确的节点
  5. 故障转移和重新分配:当一个主节点失效时,其从节点会自动升级为新的主节点,并重新分配数据
  6. 可扩展伸缩性:Redis Cluster 可以通过增加新的节点来线性扩展
  7. 无中心架构:去中心化,无中心架构,高可用的实现不需要额外引入集群(类似哨兵集群),主节点中每台机器都有哨兵节点的作用,客户端可与集群主节点的任何一台机器通信,完成数据的写入读写

2.cluster集群缺点

  1. 数据分布不均匀:Redis Cluster 的数据分布基于哈希槽,可能导致数据分布不均,可能导致在某些节点上的负载比其他节点更高(请求倾斜)。可能产生数据倾斜的原因: 节点和槽分配严重不均,不同槽对应键数量差异过大,集合对象包含大量元素等。
  2. 不支持多个数据库:Redis Cluster 不支持多个数据库,所有数据都在数据库0上。
  3. 不保证强一致性:Redis Cluster 数据通过异步复制,不保证数据的强一致性,可能会出现写入丢失的情况。
  4. 不支持Lua脚本执行:Redis Cluster 不支持在所有节点上执行Lua脚本
  5. 数据分析,隔离困难:多个业务使用同一套集群的时候,不能够依据统计区分冷热数据,资源隔离性较差,非常容易出现互相影响,Key批量操作限制,事务操作支持有限,避免产生hot-key,导致主库节点成为系统的短板。避免产生big-key,导致网卡撑爆、慢查询
  6. 带宽消耗:集群内Gossip消息通信本身会消耗带宽, 官方建议集群最大规模在1000以内, 也是出于对消息通信成本的考虑, 因此单集群不适合部署超大规模的节点。集群内所有节点通过ping/pong消息彼此交换信息, 节点间消息通信对带宽的消耗体现在以下几个方面:
    消息发送频率:跟cluster-node-timeout密切相关, 当节点发现与其他节点最后通信时间超过cluster-node-timeout/2时会直接发送ping消息。
    消息数据量:每个消息主要的数据占用包含: slots槽数组(2KB空间) 和整个集群1/10的状态数据(10个节点状态数据约1KB) 。
  7. 运维复杂:Redis Cluster 运维较复杂,例如,节点的增加和删除都需要对集群的重分配和重新平衡。

总结

以上就是今天主要讲的内容,简单介绍一下什么是Redis的Cluster集群,集群的特点,应用场景,如何搭建Cluster集群,以及常见的Clusetr集群操作。结合上篇文章 Redis集群之主从架构
Redis集群(Cluster)模式 适用于需要横向扩展和高可用性的场景,通过数据分片和数据复制来提供扩展性和容错性;
Redis主从模式 适用于读写分离和数据备份的场景,通过数据复制和读写分离来提高读取性能和数据的冗余备份能力。
具体选择哪种模式取决于应用的需求和对于扩展性、高可用性和读取性能的重要性。

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐