1 集群模式
Redis集群是一个提供在多个Redis节点之间共享数据的程序集。它并不像Redis主从复制模式那样只提供一个master节点提供写服务,而是会提供多个master节点提供写服务,每个master节点中存储的数据都不一样,这些数据通过数据分片的方式被自动分割到不同的master节点上。
1.1 集群的优点
- 负载均衡(降低单台Redis服务器的访问压力)
- 可拓展性(降低单台Redis服务器的存储压力)
- 容灾处理
1.2 集群模式实现的三种方式
我们一般要实现一个Redis集群,可以有三种方式:客户端实现、Proxy代理层、服务端实现。
1.2.1 客户端实现
我们通过代码的实现方式,实现集群访问,如下图所示:
这样的访问方式都通过代码来维护集群以及访问路径,可是这样的方式 维护难度大,也不支持动态扩容,因为一切都以代码实现访问规划。
1.2.2 Proxy代理层
如图所示:
我们在Redis和我们的客户端之间新加了一层Proxy,我们通过Proxy去规划访问,这样我们在代码层面以及Redis部署层面就无需处理,而且市面上也提供了这样的中间件,如Codis(国产)、Twemproxy。我们看下Codis的架构图,就可以知道这个插件是如何工作的,如图所示:
可见Codis是一个很完善的集群管理实现,我们可以不关心具体分片规则,程序开发容易简单,可是这样也有它的一些缺点:
- 相对单机、主从、哨兵可以看到,原先可以直接访问Redis,现在由于多了一层Proxy,所有访问要经过Proxy中转,性能下降。
- 我们需要依赖以及配置额外的插件(中间件),增加了系统复杂度。
Codis的GitHub地址:https://github.com/CodisLabs/codis
1.2.3服务端实现
服务端的实现方式就是标准的集群(分区分片)模式,RedisCluster是Redis在3.0版本后推出的分布式解决方案。
1.2.3.1 集群结构
Cluster模式实现了Redis的分布式存储,即每台节点存储不同的内容,来解决在线扩容的问题。如图:
RedisCluster采用无中心结构,它的特点如下:
- 所有的redis节点彼此互联(PING-PONG机制),内部使用二进制协议优化传输速度和带宽。
- 节点的fail是通过集群中超过半数的节点检测失效时才生效。
- 客户端与redis节点直连,不需要中间代理层.客户端不需要连接集群所有节点,连接集群中任何一个可用节点即可。
1.2.3.2 集群部署架构
为了保证集群的高可用,每个master节点下面还需要添加至少1个slave节点,这样当某个master节点发生故障后,可以从它的slave节点中选举一个作为新的master节点继续提供服务。不过当某个master节点和它下面所有的slave节点都发生故障时,整个集群就不可用了。这样就组成了下图中的结构模式:
为什么需要三主三从?
因为内部存在一个投票机制,节点的有效性是需要靠投票的,一个节点无法给自己投票,两个节点互相都连不上,A=>B,B=>A互相都说对方挂了没有定论.三个节点才能满足投票的需求。
1.3 Redis集群工作机制
1.3.1 具体工作机制
Cluster模式的具体工作机制:
- Redis集群中引入了哈希槽的概念,在Redis的每个节点上,都有一个插槽(slot),总共16384个哈希槽,取值范围为0-16383。如下图所示,跟前三种模式不同,Redis不再是默认有16(0-15)个库,也没有默认的0库说法,而是采用Slot的设计(一个集群有多个主从节点,一个主从节点上会分配多个Slot槽,每个槽点上存的是Key-Value数据):
- 当我们存取key的时候,每个key会通过CRC16校验后再对16384取模来决定放置在哪个槽,搭建Redis集群时会先给集群中每个master节点分配一部分哈希槽。比如当前集群有3个master节点,master1节点包含0~5500号哈希槽,master2节点包含5501~11000号哈希槽,master3节点包含11001~16384号哈希槽,当我们执行“set key value”时,假如 CRC16(key) % 16384 = 777,那么这个key就会被分配到master1节点上,如下图:
- 为了保证高可用,Cluster模式也引入主从复制模式,一个主节点对应一个或者多个从节点,当主节点宕机的时候,就会启用从节点。
- 当其它主节点ping一个主节点A时,如果半数以上的主节点与A通信超时,那么认为主节点A宕机了。如果主节点A和它的从节点都宕机了,那么该集群就无法再提供服务了
Cluster模式集群节点最小配置6个节点(3主3从,因为需要半数以上),其中主节点提供读写操作,从节点作为备用节点,不提供请求,只作为故障转移使用。关于集群中的涉及动态选举的机制,请参考地址:Raft
1.3.2 Redis集群中节点的通信
既然Redis集群中的数据是通过哈希槽的方式分开存储的,那么集群中每个节点都需要知道其他所有节点的状态信息,包括当前集群状态、集群中各节点负责的哈希槽、集群中各节点的master-slave状态、集群中各节点的存活状态等。Redis集群中,节点之间通过建立TCP连接,使用gossip协议来传播集群的信息。如下图:
所谓gossip协议,指的是一种消息传播的机制,类似人们传递八卦消息似的,一传十,十传百,直至所有人都知道这条八卦内容。Redis集群中各节点之间传递消息就是基于gossip协议,最终达到所有节点都会知道整个集群完整的信息。gossip协议有4种常用的消息类型:PING、PONG、MEET、FAIL。
- MEET:当需要向集群中加入新节点时,需要集群中的某个节点发送MEET消息到新节点,通知新节点加入集群。新节点收到MEET消息后,会回复PONG命令给发送者。
- PING:集群内每个节点每秒会向其他节点发送PING消息,用来检测其他节点是否正常工作,并交换彼此的状态信息。PING消息中会包括自身节点的状态数据和其他部分节点的状态数据。
- PONG:当接收到PING消息和MEET消息时,会向发送发回复PONG消息,PONG消息中包括自身节点的状态数据。节点也可以通过广播的方式发送自身的PONG消息来通知整个集群对自身状态的更新。
- FAIL:当一个节点判定另一个节点下线时,会向集群内广播一个FAIL消息,其他节点接收到FAIL消息之后,把对应节点更新为下线状态。
1.3.3 Redis集群的MOVED重定向
Redis客户端可以向Redis集群中的任意master节点发送操作指令,可以向所有节点(包括slave节点)发送查询指令。当Redis节点接收到相关指令时,会先计算key落在哪个哈希槽上(对key进行CRC16校验后再对16384取模),如果key计算出的哈希槽恰好在自己节点上,那么就直接处理指令并返回结果,如果key计算出的哈希槽不在自己节点上,那么当前节点就会查看它内部维护的哈希槽与节点ID之间的映射关系,然后给客户端返回一个MOVED错误:MOVED [哈希槽] [节点IP:端口]。这个错误包含操作的key所属的哈希槽和能处理这个请求的Redis节点的IP和端口号,例如“MOVED 3999 127.0.0.1:6379”,客户端需要根据这个信息重新发送查询指令到给定的IP和端口的Redis节点。
1.4 集群模式优缺点
优点:
- 无中心架构;
- 数据按照slot存储分布在多个节点,节点间数据共享,可动态调整数据分布;
- 可扩展性:可线性扩展到1000多个节点,节点可动态添加或删除;
- 高可用性:部分节点不可用时,集群仍可用。通过增加Slave做standby数据副本,能够实现故障自动failover,节点之间通过gossip协议交换状态信息,用投票机制完成Slave到Master的角色提升;
- 降低运维成本,提高系统的扩展性和可用性。
缺点:
- Client实现复杂,驱动要求实现Smart Client,缓存slots mapping信息并及时更新,提高了开发难度,客户端的不成熟影响业务的稳定性。目前仅JedisCluster相对成熟,异常处理部分还不完善,比如常见的“max redirect exception”。
- 节点会因为某些原因发生阻塞(阻塞时间大于clutser-node-timeout),被判断下线,这种failover是没有必要的。
- 数据通过异步复制,不保证数据的强一致性。
- 多个业务使用同一套集群时,无法根据统计区分冷热数据,资源隔离性较差,容易出现相互影响的情况。
- Slave在集群中充当“冷备”,不能缓解读压力,当然可以通过SDK的合理设计来提高Slave资源的利用率。
- Key批量操作限制,如使用mset、mget目前只支持具有相同slot值的Key执行批量操作。对于映射为不同slot值的Key由于Keys不支持跨slot查询,所以执行mset、mget、sunion等操作支持不友好。
- Key事务操作支持有限,只支持多key在同一节点上的事务操作,当多个Key分布于不同的节点上时无法使用事务功能。
- Key作为数据分区的最小粒度,不能将一个很大的键值对象如hash、list等映射到不同的节点。
- 不支持多数据库空间,单机下的redis可以支持到16个数据库,集群模式下只能使用1个数据库空间,即db 0。
- 复制结构只支持一层,从节点只能复制主节点,不支持嵌套树状复制结构。
- 避免产生hot-key,导致主库节点成为系统的短板。
- 避免产生big-key,导致网卡撑爆、慢查询等。
- 重试时间应该大于cluster-node-time时间。
- Redis Cluster不建议使用pipeline和multi-keys操作,减少max redirect产生的场景。
1.5 Cluster模式的常见问题
集群内部采用Gossip协议(谣言传播)协同自动化修复集群的状态,但Gossip有消息延时和消息冗余的问题,在集群节点数量过多的时候,节点之间需要不断进行PING/PANG通讯,不必须要的流量占用了大量的网络资源。
Redis Cluster可以进行节点的动态扩容缩容,在扩缩容的时候,就需要进行数据迁移。而Redis 为了保证迁移的一致性, 迁移所有操作都是同步操作,执行迁移时,两端的 Redis 均会进入时长不等的 阻塞状态。对于小 Key,该时间可以忽略不计,但如果一旦 Key 的内存使用过大,严重的时候会接触发集群内的故障转移,造成不必要的切换.
Gossip 的缺陷 - 消息的延迟 由于 Gossip 协议中,节点只会随机向少数几个节点发送消息,消息最终是通过多 个轮次的散播而到达全网的。 因此使用 Gossip 协议会造成不可避免的消息延迟。不适合用在对实时性要求较高 的场景下。
消息冗余 Gossip 协议规定,节点会定期随机选择周围节点发送消息,而收到消息的节点也会重复该步骤。 因此存在消息重复发送给同一节点的情况,造成了消息的冗余,同时也增加了收到消息的节点的处理压力。 而且,由于是定期发送而且不反馈,因此即使节点收到了消息,还是会反复收到重复消息,加重了消息的冗余。
2 Redis集群安装与配置
2.1 集群部署
2.1.1 集群安装
首先我们复制6份redis.conf配置文件,分别命名为redis7001.conf、redis7002.conf、redis7003.conf、redis7004.conf、redis7005.conf、redis7006conf,存放在自己想要放的地方。
并按照下列配置信息修改配置文件:
代码语言:javascript复制#修改成自己对应的端口号
port
#指定了记录日志的文件。
logfile
#数据目录,数据库的写入会在这个目录。rdb、aof文件也会写在这个目录
dir
#是否开启集群
cluster-enabled
#集群配置文件的名称,每个节点都有一个集群相关的配置文件,持久化保存集群的信息。
#这个文件并不需要手动配置,这个配置文件由Redis生成并更新,每个Redis集群节点需要一个单独的配置文件,请确保与实例运行的系统中配置文件名称不冲突(建议配对应端口号)
cluster-config-file nodes-6379.conf
#节点互连超时的阀值。集群节点超时毫秒数
cluster-node-timeout 5000
#默认redis使用的是rdb方式持久化,这种方式在许多应用中已经足够用了。
#但是redis如果中途宕机,会导致可能有几分钟的数据丢失,根据save来策略进行持久化,Append Only File是另一种持久化方式,可以提供更好的持久化特性。
#Redis会把每次写入的数据在接收后都写入 appendonly.aof 文件,每次启动时Redis都会先把这个文件的数据读入内存里,先忽略RDB文件。
appendonly
appendonly yes
protected-mode no #保护模式 yes改为no
#bind 127.0.0.1 #注释或者去掉这个
daemonize yes #用来指定redis是否要用守护线程的方式启动,yes表示后台启动
执行命令分别启动所有节点,如:
代码语言:javascript复制/Volumes/work/redis-5.0.8/src/redis-server
/Volumes/work/redis-5.0.8/conf/7001/redis7001.conf
/Volumes/work/redis-5.0.8/src/redis-server
/Volumes/work/redis-5.0.8/conf/7002/redis7002.conf
/Volumes/work/redis-5.0.8/src/redis-server
/Volumes/work/redis-5.0.8/conf/7003/redis7003.conf
/Volumes/work/redis-5.0.8/src/redis-server
/Volumes/work/redis-5.0.8/conf/7004/redis7004.conf
/Volumes/work/redis-5.0.8/src/redis-server
/Volumes/work/redis-5.0.8/conf/7005/redis7005.conf
/Volumes/work/redis-5.0.8/src/redis-server
/Volumes/work/redis-5.0.8/conf/7006/redis7006.conf
可以查看到进程:
代码语言:javascript复制192:conf houjing$ ps -ef |grep redis
501 43005 1 0 5:46上午 ?? 0:00.20 /Volumes/work/redis-5.0.8/src/redis-server *:7001 [cluster]
501 43018 1 0 5:47上午 ?? 0:00.02 /Volumes/work/redis-5.0.8/src/redis-server *:7002 [cluster]
501 43020 1 0 5:47上午 ?? 0:00.02 /Volumes/work/redis-5.0.8/src/redis-server *:7003 [cluster]
501 43022 1 0 5:47上午 ?? 0:00.02 /Volumes/work/redis-5.0.8/src/redis-server *:7004 [cluster]
501 43024 1 0 5:47上午 ?? 0:00.02 /Volumes/work/redis-5.0.8/src/redis-server *:7005 [cluster]
501 43026 1 0 5:47上午 ?? 0:00.01 /Volumes/work/redis-5.0.8/src/redis-server *:7006 [cluster]
执行命令创建集群:
代码语言:javascript复制/Volumes/work/redis-5.0.8/src/redis-cli --cluster create 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
--cluster-replicas 1 # 1表示每个主节点至少一个备份
可以得到如下结果:
代码语言:javascript复制192:conf houjing$ /Volumes/work/redis-5.0.8/src/redis-cli --cluster create 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 --cluster-replicas 1
>>> Performing hash slots allocation on 6 nodes...
Master[0] -> Slots 0 - 5460
Master[1] -> Slots 5461 - 10922
Master[2] -> Slots 10923 - 16383
Adding replica 127.0.0.1:7005 to 127.0.0.1:7001
Adding replica 127.0.0.1:7006 to 127.0.0.1:7002
Adding replica 127.0.0.1:7004 to 127.0.0.1:7003
>>> Trying to optimize slaves allocation for anti-affinity
[WARNING] Some slaves are in the same host as their master
M: 4c7fcd06c55ceec27b5bbc450b99159282e7783e 127.0.0.1:7001
slots:[0-5460] (5461 slots) master
M: d7a0d77d2f0fa05ccf4a14e379b52044b212607e 127.0.0.1:7002
slots:[5461-10922] (5462 slots) master
M: 6494df31d4ed564d80947c9d39369cc7cfb6c0e6 127.0.0.1:7003
slots:[10923-16383] (5461 slots) master
S: 9d6d78888e579442b209fca95af13b6ece4810b6 127.0.0.1:7004
replicates 6494df31d4ed564d80947c9d39369cc7cfb6c0e6
S: 8891bfe505da42298be048364ed003b83dc52139 127.0.0.1:7005
replicates 4c7fcd06c55ceec27b5bbc450b99159282e7783e
S: 981ad9769245c140c0bd9ee239472dc6e2cb07eb 127.0.0.1:7006
replicates d7a0d77d2f0fa05ccf4a14e379b52044b212607e
Can I set the above configuration? (type 'yes' to accept): yes 此处输入yes
>>> 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:7001)
M: 4c7fcd06c55ceec27b5bbc450b99159282e7783e 127.0.0.1:7001
slots:[0-5460] (5461 slots) master
1 additional replica(s)
S: 981ad9769245c140c0bd9ee239472dc6e2cb07eb 127.0.0.1:7006
slots: (0 slots) slave
replicates d7a0d77d2f0fa05ccf4a14e379b52044b212607e
M: 6494df31d4ed564d80947c9d39369cc7cfb6c0e6 127.0.0.1:7003
slots:[10923-16383] (5461 slots) master
1 additional replica(s)
S: 9d6d78888e579442b209fca95af13b6ece4810b6 127.0.0.1:7004
slots: (0 slots) slave
replicates 6494df31d4ed564d80947c9d39369cc7cfb6c0e6
M: d7a0d77d2f0fa05ccf4a14e379b52044b212607e 127.0.0.1:7002
slots:[5461-10922] (5462 slots) master
1 additional replica(s)
S: 8891bfe505da42298be048364ed003b83dc52139 127.0.0.1:7005
slots: (0 slots) slave
replicates 4c7fcd06c55ceec27b5bbc450b99159282e7783e
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
2.1.2 集群检查
可以执行命令 ./redis-cli -c -p 7001 进行登录,然后查看集群信息以及slot分配信息:
代码语言:javascript复制192:src houjing$ ./redis-cli -c -p 7001
127.0.0.1:7001> cluster nodes
981ad9769245c140c0bd9ee239472dc6e2cb07eb 127.0.0.1:7006@17006 slave d7a0d77d2f0fa05ccf4a14e379b52044b212607e 0 1586210070424 6 connected
4c7fcd06c55ceec27b5bbc450b99159282e7783e 127.0.0.1:7001@17001 myself,master - 0 1586210069000 1 connected 0-5460
6494df31d4ed564d80947c9d39369cc7cfb6c0e6 127.0.0.1:7003@17003 master - 0 1586210071437 3 connected 10923-16383
9d6d78888e579442b209fca95af13b6ece4810b6 127.0.0.1:7004@17004 slave 6494df31d4ed564d80947c9d39369cc7cfb6c0e6 0 1586210069416 4 connected
d7a0d77d2f0fa05ccf4a14e379b52044b212607e 127.0.0.1:7002@17002 master - 0 1586210070000 2 connected 5461-10922
8891bfe505da42298be048364ed003b83dc52139 127.0.0.1:7005@17005 slave 4c7fcd06c55ceec27b5bbc450b99159282e7783e 0 1586210070000 5 connected
可以根据以上得出总共有3个master节点,每个节点有一个slave节点,三个master节点分别分配了0-5460、5461-10922、10923-16383三个slot范围,7001这个master节点的slave节点为7005节点,其id为4c7fcd06c55ceec27b5bbc450b99159282e7783e。
添加数据,可以看到数据会根hash然后存到对应的slot槽,也就是存到slot槽对应的节点上,如下所示:
代码语言:javascript复制127.0.0.1:7001> set 789 789
-> Redirected to slot [11123] located at 127.0.0.1:7003
OK
127.0.0.1:7003> set 123 123
-> Redirected to slot [5970] located at 127.0.0.1:7002
OK
127.0.0.1:7002> set 365 365
-> Redirected to slot [5424] located at 127.0.0.1:7001
OK
当然每个节点也只有自己的那部分数据:
代码语言:javascript复制127.0.0.1:7001> keys *
1) "365"
2.1.3 宕机测试
我们通过手动杀掉节点7001的进程:
代码语言:javascript复制192:bin houjing$ kill 43005
192:bin houjing$ ps -ef|grep redis
501 43018 1 0 5:47上午 ?? 0:03.91 /Volumes/work/redis-5.0.8/src/redis-server *:7002 [cluster]
501 43020 1 0 5:47上午 ?? 0:03.94 /Volumes/work/redis-5.0.8/src/redis-server *:7003 [cluster]
501 43022 1 0 5:47上午 ?? 0:03.95 /Volumes/work/redis-5.0.8/src/redis-server *:7004 [cluster]
501 43024 1 0 5:47上午 ?? 0:03.92 /Volumes/work/redis-5.0.8/src/redis-server *:7005 [cluster]
501 43026 1 0 5:47上午 ?? 0:03.91 /Volumes/work/redis-5.0.8/src/redis-server *:7006 [cluster]
然后查看集群信息:
代码语言:javascript复制127.0.0.1:7002> cluster nodes
8891bfe505da42298be048364ed003b83dc52139 127.0.0.1:7005@17005 master - 0 1586210953749 7 connected 0-5460
9d6d78888e579442b209fca95af13b6ece4810b6 127.0.0.1:7004@17004 slave 6494df31d4ed564d80947c9d39369cc7cfb6c0e6 0 1586210953547 4 connected
981ad9769245c140c0bd9ee239472dc6e2cb07eb 127.0.0.1:7006@17006 slave d7a0d77d2f0fa05ccf4a14e379b52044b212607e 0 1586210952000 6 connected
d7a0d77d2f0fa05ccf4a14e379b52044b212607e 127.0.0.1:7002@17002 myself,master - 0 1586210952000 2 connected 5461-10922
6494df31d4ed564d80947c9d39369cc7cfb6c0e6 127.0.0.1:7003@17003 master - 0 1586210952538 3 connected 10923-16383
4c7fcd06c55ceec27b5bbc450b99159282e7783e 127.0.0.1:7001@17001 master,fail - 1586210920633 1586210919825 1 disconnected
可以看到原先7001节点断开连接,其slave节点7005顶替了7001节点,变成了master节点,并继承了原先7001节点的slot槽。
当我们重新启动7001节点后,7001节点便作为7005的slave节点重新上线:
代码语言:javascript复制127.0.0.1:7002> cluster nodes
8891bfe505da42298be048364ed003b83dc52139 127.0.0.1:7005@17005 master - 0 1586211138837 7 connected 0-5460
9d6d78888e579442b209fca95af13b6ece4810b6 127.0.0.1:7004@17004 slave 6494df31d4ed564d80947c9d39369cc7cfb6c0e6 0 1586211138331 4 connected
981ad9769245c140c0bd9ee239472dc6e2cb07eb 127.0.0.1:7006@17006 slave d7a0d77d2f0fa05ccf4a14e379b52044b212607e 0 1586211139548 6 connected
d7a0d77d2f0fa05ccf4a14e379b52044b212607e 127.0.0.1:7002@17002 myself,master - 0 1586211138000 2 connected 5461-10922
6494df31d4ed564d80947c9d39369cc7cfb6c0e6 127.0.0.1:7003@17003 master - 0 1586211139000 3 connected 10923-16383
4c7fcd06c55ceec27b5bbc450b99159282e7783e 127.0.0.1:7001@17001 slave 8891bfe505da42298be048364ed003b83dc52139 0 1586211137522 7 connected
2.1.4 水平扩展
由于我们Redis集群是有能力限制的,当不能满足当下要求的时候,就需要进行扩容,这事就涉及到水平扩展,我们先增加两份配置文件redis7007.conf、redis7008.conf,并创建两个节点并启动:
代码语言:javascript复制192:conf houjing$ /Volumes/work/redis-5.0.8/src/redis-server /Volumes/work/redis-5.0.8/conf/7007/redis7007.conf
192:conf houjing$ /Volumes/work/redis-5.0.8/src/redis-server /Volumes/work/redis-5.0.8/conf/7008/redis7008.conf
192:conf houjing$ ps -ef |grep redis
501 43018 1 0 5:47上午 ?? 0:06.27 /Volumes/work/redis-5.0.8/src/redis-server *:7002 [cluster]
501 43020 1 0 5:47上午 ?? 0:06.29 /Volumes/work/redis-5.0.8/src/redis-server *:7003 [cluster]
501 43022 1 0 5:47上午 ?? 0:06.29 /Volumes/work/redis-5.0.8/src/redis-server *:7004 [cluster]
501 43024 1 0 5:47上午 ?? 0:06.25 /Volumes/work/redis-5.0.8/src/redis-server *:7005 [cluster]
501 43026 1 0 5:47上午 ?? 0:06.24 /Volumes/work/redis-5.0.8/src/redis-server *:7006 [cluster]
501 43109 1 0 6:12上午 ?? 0:01.31 /Volumes/work/redis-5.0.8/src/redis-server *:7001 [cluster]
501 43141 1 0 6:18上午 ?? 0:00.03 /Volumes/work/redis-5.0.8/src/redis-server *:7007 [cluster]
501 43143 1 0 6:18上午 ?? 0:00.03 /Volumes/work/redis-5.0.8/src/redis-server *:7008 [cluster]
但是我们可以看到虽然进程启动了,但是并未加到集群中去:
代码语言:javascript复制127.0.0.1:7002> cluster nodes
8891bfe505da42298be048364ed003b83dc52139 127.0.0.1:7005@17005 master - 0 1586211626238 7 connected 0-5460
9d6d78888e579442b209fca95af13b6ece4810b6 127.0.0.1:7004@17004 slave 6494df31d4ed564d80947c9d39369cc7cfb6c0e6 0 1586211626741 4 connected
981ad9769245c140c0bd9ee239472dc6e2cb07eb 127.0.0.1:7006@17006 slave d7a0d77d2f0fa05ccf4a14e379b52044b212607e 0 1586211626036 6 connected
d7a0d77d2f0fa05ccf4a14e379b52044b212607e 127.0.0.1:7002@17002 myself,master - 0 1586211625000 2 connected 5461-10922
6494df31d4ed564d80947c9d39369cc7cfb6c0e6 127.0.0.1:7003@17003 master - 0 1586211626540 3 connected 10923-16383
4c7fcd06c55ceec27b5bbc450b99159282e7783e 127.0.0.1:7001@17001 slave 8891bfe505da42298be048364ed003b83dc52139 0 1586211627751 7 connected
执行集群节点并没有返现我们的节点数据,这个时候需要把启动的节点数据增加到集群中去,执行命令:./redis-cli --cluster add-node 127.0.0.1:7007 127.0.0.1:7005:
代码语言:javascript复制192:src houjing$ ./redis-cli --cluster add-node 127.0.0.1:7007 127.0.0.1:7001
>>> Adding node 127.0.0.1:7007 to cluster 127.0.0.1:7001
>>> Performing Cluster Check (using node 127.0.0.1:7001)
M: e83dc175f217148444a07ed367fea54ed6b3e2aa 127.0.0.1:7001
slots:[0-5460] (5461 slots) master
1 additional replica(s)
S: 4686809f8673bc918f03fd8642c410c1ca647a39 127.0.0.1:7004
slots: (0 slots) slave
replicates a3b2d4619e3ad17ea86f69a05543b12aa722a864
S: 10eed7c7bbbd7e9e427d0ccf7e2d557607d7467f 127.0.0.1:7005
slots: (0 slots) slave
replicates de6820c4780a070ac658ee4bdf98c44b46038d81
M: de6820c4780a070ac658ee4bdf98c44b46038d81 127.0.0.1:7003
slots:[10923-16383] (5461 slots) master
1 additional replica(s)
S: 9624c69e40a36dd1d67fbd21cae75753735f3f02 127.0.0.1:7006
slots: (0 slots) slave
replicates e83dc175f217148444a07ed367fea54ed6b3e2aa
M: a3b2d4619e3ad17ea86f69a05543b12aa722a864 127.0.0.1:7002
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.
>>> Send CLUSTER MEET to node 127.0.0.1:7007 to make it join the cluster.
[OK] New node added correctly.
127.0.0.1:7005> cluster nodes
de6820c4780a070ac658ee4bdf98c44b46038d81 127.0.0.1:7003@17003 master - 0 1586212133524 3 connected 10923-16383
e83dc175f217148444a07ed367fea54ed6b3e2aa 127.0.0.1:7001@17001 master - 0 1586212134536 1 connected 0-5460
a3b2d4619e3ad17ea86f69a05543b12aa722a864 127.0.0.1:7002@17002 master - 0 1586212133323 2 connected 5461-10922
4686809f8673bc918f03fd8642c410c1ca647a39 127.0.0.1:7004@17004 slave a3b2d4619e3ad17ea86f69a05543b12aa722a864 0 1586212133000 4 connected
a8236394ddd7f210e4df2036bd73968503283cf8 127.0.0.1:7007@17007 master - 0 1586212134333 0 connected
10eed7c7bbbd7e9e427d0ccf7e2d557607d7467f 127.0.0.1:7005@17005 myself,slave de6820c4780a070ac658ee4bdf98c44b46038d81 0 1586212134000 5 connected
9624c69e40a36dd1d67fbd21cae75753735f3f02 127.0.0.1:7006@17006 slave e83dc175f217148444a07ed367fea54ed6b3e2aa 0 1586212134000 6 connected
可以看到7007成功加入到集群节点,但是并未分配slot槽,接下来使用redis-cli命令为7007分配hash槽,找到集群中的任意一个主节点(7001),对其进行重新分片工作。命令: ./redis-cli --cluster reshard 127.0.0.1:7001
代码语言:javascript复制192:src houjing$ ./redis-cli --cluster reshard 127.0.0.1:7001
>>> Performing Cluster Check (using node 127.0.0.1:7001)
M: e83dc175f217148444a07ed367fea54ed6b3e2aa 127.0.0.1:7001
slots:[0-5460] (5461 slots) master
1 additional replica(s)
S: 4686809f8673bc918f03fd8642c410c1ca647a39 127.0.0.1:7004
slots: (0 slots) slave
replicates a3b2d4619e3ad17ea86f69a05543b12aa722a864
S: 10eed7c7bbbd7e9e427d0ccf7e2d557607d7467f 127.0.0.1:7005
slots: (0 slots) slave
replicates de6820c4780a070ac658ee4bdf98c44b46038d81
M: de6820c4780a070ac658ee4bdf98c44b46038d81 127.0.0.1:7003
slots:[10923-16383] (5461 slots) master
1 additional replica(s)
S: 9624c69e40a36dd1d67fbd21cae75753735f3f02 127.0.0.1:7006
slots: (0 slots) slave
replicates e83dc175f217148444a07ed367fea54ed6b3e2aa
M: a8236394ddd7f210e4df2036bd73968503283cf8 127.0.0.1:7007
slots: (0 slots) master
M: a3b2d4619e3ad17ea86f69a05543b12aa722a864 127.0.0.1:7002
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.
How many slots do you want to move (from 1 to 16384)? 1000
What is the receiving node ID? a8236394ddd7f210e4df2036bd73968503283cf8
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
以上就是要从其余三个master上划分1000个slot到7007节点,7007节点的ID为a8236394ddd7f210e4df2036bd73968503283cf8。两种reshard方式如下:
- 一种是all,以将所有节点用作散列槽的源节点,
- 一种是done,这种是你自己选择从哪个节点上拿出来节点分给8007
备注:all是随机的,比如说我们要分出1000个,则3个主节点分别拿出333个,333个,334个节点分别给7007。
此时查看cluster nodes信息如下:
代码语言:javascript复制127.0.0.1:7005> cluster nodes
de6820c4780a070ac658ee4bdf98c44b46038d81 127.0.0.1:7003@17003 master - 0 1586212646000 3 connected 11256-16383
e83dc175f217148444a07ed367fea54ed6b3e2aa 127.0.0.1:7001@17001 master - 0 1586212645158 1 connected 333-5460
a3b2d4619e3ad17ea86f69a05543b12aa722a864 127.0.0.1:7002@17002 master - 0 1586212646000 2 connected 5795-10922
4686809f8673bc918f03fd8642c410c1ca647a39 127.0.0.1:7004@17004 slave a3b2d4619e3ad17ea86f69a05543b12aa722a864 0 1586212646572 4 connected
a8236394ddd7f210e4df2036bd73968503283cf8 127.0.0.1:7007@17007 master - 0 1586212646372 7 connected 0-332 5461-5794 10923-11255
10eed7c7bbbd7e9e427d0ccf7e2d557607d7467f 127.0.0.1:7005@17005 myself,slave de6820c4780a070ac658ee4bdf98c44b46038d81 0 1586212645000 5 connected
9624c69e40a36dd1d67fbd21cae75753735f3f02 127.0.0.1:7006@17006 slave e83dc175f217148444a07ed367fea54ed6b3e2aa 0 1586212645000 6 connected
可以看到7007节点拿到了3段合计1000个slot槽,接下来我们为7007增加从节点7008,依旧是先把7008添加到集群中去:./redis-cli --cluster add-node 127.0.0.1:7008 127.0.0.1:7001:
代码语言:javascript复制127.0.0.1:7005> cluster nodes
d9daa198ebe389f7c707e6d41c8052e59f29ce0b 127.0.0.1:7008@17008 master - 0 1586212814000 0 connected
de6820c4780a070ac658ee4bdf98c44b46038d81 127.0.0.1:7003@17003 master - 0 1586212814970 3 connected 11256-16383
e83dc175f217148444a07ed367fea54ed6b3e2aa 127.0.0.1:7001@17001 master - 0 1586212815000 1 connected 333-5460
a3b2d4619e3ad17ea86f69a05543b12aa722a864 127.0.0.1:7002@17002 master - 0 1586212815000 2 connected 5795-10922
4686809f8673bc918f03fd8642c410c1ca647a39 127.0.0.1:7004@17004 slave a3b2d4619e3ad17ea86f69a05543b12aa722a864 0 1586212814565 4 connected
a8236394ddd7f210e4df2036bd73968503283cf8 127.0.0.1:7007@17007 master - 0 1586212815574 7 connected 0-332 5461-5794 10923-11255
10eed7c7bbbd7e9e427d0ccf7e2d557607d7467f 127.0.0.1:7005@17005 myself,slave de6820c4780a070ac658ee4bdf98c44b46038d81 0 1586212815000 5 connected
9624c69e40a36dd1d67fbd21cae75753735f3f02 127.0.0.1:7006@17006 slave e83dc175f217148444a07ed367fea54ed6b3e2aa 0 1586212814565 6 connected
可以看到7008添加到了集群,但是目前还是主节点,并且没有hash槽。接下来需要让其变成从节点。
命令:CLUSTER REPLICATE a8236394ddd7f210e4df2036bd73968503283cf8(这个节点给哪个主节点)
代码语言:javascript复制192:src houjing$ ./redis-cli -c -p 7008
127.0.0.1:7008> CLUSTER REPLICATE a8236394ddd7f210e4df2036bd73968503283cf8
OK
127.0.0.1:7008> cluster nodes
4686809f8673bc918f03fd8642c410c1ca647a39 127.0.0.1:7004@17004 slave a3b2d4619e3ad17ea86f69a05543b12aa722a864 0 1586213074000 2 connected
a8236394ddd7f210e4df2036bd73968503283cf8 127.0.0.1:7007@17007 master - 0 1586213073000 7 connected 0-332 5461-5794 10923-11255
9624c69e40a36dd1d67fbd21cae75753735f3f02 127.0.0.1:7006@17006 slave e83dc175f217148444a07ed367fea54ed6b3e2aa 0 1586213073000 1 connected
de6820c4780a070ac658ee4bdf98c44b46038d81 127.0.0.1:7003@17003 master - 0 1586213074818 3 connected 11256-16383
e83dc175f217148444a07ed367fea54ed6b3e2aa 127.0.0.1:7001@17001 master - 0 1586213074000 1 connected 333-5460
d9daa198ebe389f7c707e6d41c8052e59f29ce0b 127.0.0.1:7008@17008 myself,slave a8236394ddd7f210e4df2036bd73968503283cf8 0 1586213073000 0 connected
a3b2d4619e3ad17ea86f69a05543b12aa722a864 127.0.0.1:7002@17002 master - 0 1586213074513 2 connected 5795-10922
10eed7c7bbbd7e9e427d0ccf7e2d557607d7467f 127.0.0.1:7005@17005 slave a8236394ddd7f210e4df2036bd73968503283cf8 0 1586213074513 7 connected
如上所示,查看cluster nodes信息则可直到成功实现Redis集群的水平扩容。
2.1.5 节点下线
我们现实中存在着水平扩展,那么当服务使用量下降时,也存在着节点下线。比如我们要让刚扩容的7007、7008下线,我们先来删除从节点7008:
命令:./redis-cli --cluster del-node 127.0.0.1:7008 d9daa198ebe389f7c707e6d41c8052e59f29ce0b
代码语言:javascript复制192:src houjing$ ./redis-cli --cluster del-node 127.0.0.1:7008 d9daa198ebe389f7c707e6d41c8052e59f29ce0b
>>> Removing node d9daa198ebe389f7c707e6d41c8052e59f29ce0b from cluster 127.0.0.1:7008
>>> Sending CLUSTER FORGET messages to the cluster...
>>> SHUTDOWN the node.
127.0.0.1:7001> cluster nodes
4686809f8673bc918f03fd8642c410c1ca647a39 127.0.0.1:7004@17004 slave a3b2d4619e3ad17ea86f69a05543b12aa722a864 0 1586213383501 4 connected
e83dc175f217148444a07ed367fea54ed6b3e2aa 127.0.0.1:7001@17001 myself,master - 0 1586213383000 1 connected 333-5460
10eed7c7bbbd7e9e427d0ccf7e2d557607d7467f 127.0.0.1:7005@17005 slave a8236394ddd7f210e4df2036bd73968503283cf8 0 1586213383000 7 connected
de6820c4780a070ac658ee4bdf98c44b46038d81 127.0.0.1:7003@17003 master - 0 1586213383000 3 connected 11256-16383
9624c69e40a36dd1d67fbd21cae75753735f3f02 127.0.0.1:7006@17006 slave e83dc175f217148444a07ed367fea54ed6b3e2aa 0 1586213384308 6 connected
a8236394ddd7f210e4df2036bd73968503283cf8 127.0.0.1:7007@17007 master - 0 1586213383300 7 connected 0-332 5461-5794 10923-11255
a3b2d4619e3ad17ea86f69a05543b12aa722a864 127.0.0.1:7002@17002 master - 0 1586213383000 2 connected 5795-10922
接下来删除主节点7007,命令:./redis-cli --cluster reshard 127.0.0.1:7007
代码语言:javascript复制192:src houjing$ ./redis-cli --cluster reshard 127.0.0.1:7007
>>> Performing Cluster Check (using node 127.0.0.1:7007)
M: a8236394ddd7f210e4df2036bd73968503283cf8 127.0.0.1:7007
slots:[172-665],[5461-6128],[10923-11588] (1828 slots) master
1 additional replica(s)
M: a3b2d4619e3ad17ea86f69a05543b12aa722a864 127.0.0.1:7002
slots:[6542-10922] (4381 slots) master
1 additional replica(s)
S: 4686809f8673bc918f03fd8642c410c1ca647a39 127.0.0.1:7004
slots: (0 slots) slave
replicates a3b2d4619e3ad17ea86f69a05543b12aa722a864
M: de6820c4780a070ac658ee4bdf98c44b46038d81 127.0.0.1:7003
slots:[12003-16383] (4381 slots) master
S: 9624c69e40a36dd1d67fbd21cae75753735f3f02 127.0.0.1:7006
slots: (0 slots) slave
replicates e83dc175f217148444a07ed367fea54ed6b3e2aa
M: e83dc175f217148444a07ed367fea54ed6b3e2aa 127.0.0.1:7001
slots:[0-171],[666-5460],[6129-6541],[11589-12002] (5794 slots) master
1 additional replica(s)
S: 10eed7c7bbbd7e9e427d0ccf7e2d557607d7467f 127.0.0.1:7005
slots: (0 slots) slave
replicates a8236394ddd7f210e4df2036bd73968503283cf8
[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)? 1828
What is the receiving node ID? a3b2d4619e3ad17ea86f69a05543b12aa722a864
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: a8236394ddd7f210e4df2036bd73968503283cf8
Source node #2: done
Ready to move 1828 slots.
可以看到我们把7007节点的1828个slot全部移到了7002上,再次查看cluster nodes信息如下:
代码语言:javascript复制127.0.0.1:7001> cluster nodes
4686809f8673bc918f03fd8642c410c1ca647a39 127.0.0.1:7004@17004 slave a3b2d4619e3ad17ea86f69a05543b12aa722a864 0 1586214201506 9 connected
e83dc175f217148444a07ed367fea54ed6b3e2aa 127.0.0.1:7001@17001 myself,master - 0 1586214200000 8 connected 0-171 666-5460 6129-6541 11589-12002
10eed7c7bbbd7e9e427d0ccf7e2d557607d7467f 127.0.0.1:7005@17005 slave a3b2d4619e3ad17ea86f69a05543b12aa722a864 0 1586214201000 9 connected
de6820c4780a070ac658ee4bdf98c44b46038d81 127.0.0.1:7003@17003 master - 0 1586214200000 3 connected 12003-16383
9624c69e40a36dd1d67fbd21cae75753735f3f02 127.0.0.1:7006@17006 slave e83dc175f217148444a07ed367fea54ed6b3e2aa 0 1586214200000 8 connected
a8236394ddd7f210e4df2036bd73968503283cf8 127.0.0.1:7007@17007 master - 0 1586214201707 7 connected
a3b2d4619e3ad17ea86f69a05543b12aa722a864 127.0.0.1:7002@17002 master - 0 1586214201000 9 connected 172-665 5461-6128 6542-11588
7007节点还在,但是分配的slot没有了,接下来关闭7007节点,命令:./redis-cli -p 7001 shutdown
代码语言:javascript复制127.0.0.1:7001> cluster nodes
a8236394ddd7f210e4df2036bd73968503283cf8 127.0.0.1:7007@17007 master - 1586214471239 1586214469000 7 disconnected
4686809f8673bc918f03fd8642c410c1ca647a39 127.0.0.1:7004@17004 slave a3b2d4619e3ad17ea86f69a05543b12aa722a864 0 1586214473159 9 connected
e83dc175f217148444a07ed367fea54ed6b3e2aa 127.0.0.1:7001@17001 myself,slave 9624c69e40a36dd1d67fbd21cae75753735f3f02 0 1586214473000 8 connected
de6820c4780a070ac658ee4bdf98c44b46038d81 127.0.0.1:7003@17003 master - 0 1586214473000 3 connected 12003-16383
9624c69e40a36dd1d67fbd21cae75753735f3f02 127.0.0.1:7006@17006 master - 0 1586214472653 10 connected 0-171 666-5460 6129-6541 11589-12002
a3b2d4619e3ad17ea86f69a05543b12aa722a864 127.0.0.1:7002@17002 master - 0 1586214474073 9 connected 172-665 5461-6128 6542-11588
10eed7c7bbbd7e9e427d0ccf7e2d557607d7467f 127.0.0.1:7005@17005 slave 9624c69e40a36dd1d67fbd21cae75753735f3f02 0 1586214474175 10 connected
于是实现了7007、7008节点的下线。
2.2 Redis-Cluster 的配置
2.2.1 redis.conf 配置
需要修改redis.conf 配置文件
cluster-enable yes # 表示这个Redis 是一个cluster节点
cluster-config-file [filename.conf] # cluster的配置文件
cluster-node-timeout 10000 # 节点服务超时时间,用于判定该节点是否下线或切换为从节点
cluster集群中各个redis依次启动完成后,需要使用
./redis-trib.rb create --replicas 1 ip1:port1 ip2:port2 ip3:port3 ip4:port4 ip5:port5 ip6:port6
create 代表创建集群
--replicas: 指定内部结构 1:代表一个master连接一个slave节点 ip1:port1->ip4:port4 ip2:port2->ip5:port5 ip3:port3->ip6:port6
2: 代表一个master连接两个slave节点 ip1:port1->ip3:port3,ip4:port4 ip2:port2->ip5:port5,ip6:port6
cluster集群搭建好之后,需要注意的是要使用 redis-cli -c 来连接 cluster集群。
2.2.2 Cluster的 slot 槽重分配
Redis-Cluster 支持动态的新增和删除节点
新增
./redis-trib.rb add-node [ip1:port1] [ip2:port],新增成功后我们可以通过 cluster nodes 命令查看集群节点信息,我们可以看到 新增的节点是没有被分配 slot槽的。
手动分配槽节点
cluster setslot 5462 node e4fee6a90d5b071ad514cd30f8d5d9a1c13947b3
cluster setslot [槽] node [目标id]
自动分配槽节点
./redis-trib.rb reshard ip:port
删除
对于已经分配了槽区间的节点,我们是无法直接删除的,需要先将槽区间分配出去,然后再进行删除
将槽区间分配到其他节点
./redis-trib.rb reshard ip:port
删除命令
./redis-trib.rb del-node ip:port 目标id
参考链接
运维:终于不用再背着数万实例的Redis集群了!_Cluster
Redis学习之4种模式实践及机制解析(单机、主从、哨兵、集群)
redis架构_剑八-的博客-CSDN博客
Redis高可用方案—主从(masterslave)架构
Redis高可用架构—哨兵(sentinel)机制详细介绍
Redis高可用架构—Redis集群(Redis Cluster)详细介绍
Redis学习之Redis集群模式缺陷及其处理
- Redis学习之4种模式实践及机制解析(单机、主从、哨兵、集群)
- Redis学习之API学习及Jedis源码原理分析
- Redis学习之Jedis源码原理分析探究(BIO手写Jedis客户端)
Redis 详解_王叮咚的博客-CSDN博客