NoSQL之Redis集群

发布于:2025-06-27 ⋅ 阅读:(21) ⋅ 点赞:(0)

目录

一:Redis 集群方式

二:集群模式简介

三:数据分片方式

1:客户端分片

2:代理分片

3:服务器端分片

四:负载均衡的实现

五:故障的处理

1:故障转移

2:多slave选举

六:Redis群集部署

创建 redis 群集

测试群集

集群信息查看

添加节点

添加节点的另一种方法

删除节点

集群排错思路


一:Redis 集群方式

Redis 有三种模式:分别是主从复制、哨兵模式、Cluster

模式 优点 缺陷
主从模式 1. 实现数据多机备份
2. 读操作负载均衡
3. 简单故障恢复
1. 故障恢复无法自动化
2. 写操作无法负载均衡
3. 存储能力受单机限制
哨兵模式 1. 在主从复制基础上实现自动化故障恢复 1. 写操作无法负载均衡
2. 存储能力受单机限制
3. 哨兵无法对从节点自动故障转移
4. 从节点故障可能导致读取服务不可用
集群模式 1. 解决写操作负载均衡问题
2. 突破单机存储限制
3. 高可用方案完善
1. 配置和管理相对复杂
2. 需要更多节点支持
3. 网络分区可能导致部分服务不可用
模式 版本支持 优点 缺点
主从模式 Redis 2.8 之前 1. 解决数据备份问题
2. 读写分离,提高服务器性能
1. Master 故障需人工介入,无法自动故障转移
2. Master 无法动态扩容
哨兵模式 Redis 2.8 及之后 1. 监测 Master 状态
2. 自动主从切换,实现故障自愈
3. 从节点自动跟随新 Master
1. Slave 节点下线时,Sentinel 不进行故障转移,客户端可能无法获取可用从节点
2. Master 无法动态扩容
Redis Cluster 模式 Redis 3.0 及之后 1. 满足分布式需求
2. 解决单机内存、并发和流量瓶颈,实现负载均衡
3. 支持动态扩容
4. 无中心化 P2P 模式
5. 通过 Gossip 协议同步节点信息
6. 自动故障转移,Slot 迁移中数据可用
7. 自动分片数据到不同节点
8. 部分节点故障仍可处理命令
1. 架构较新,最佳实践较少
2. 客户端需缓存路由表以提升性能
3. 节点发现和 Reshard 操作不够自动化
4. 不支持多 Keys 命令(需跨节点移动数据)
5. 仅支持默认 0 号数据库,不支持 SELECT 命令

二:集群模式简介

特性 Redis Cluster 集群模式
版本支持 Redis 3.0 及以上
节点组成 由多个主节点(Master)和从节点(Slave)组成,主节点负责读写和集群维护,从节点仅备份数据。
数据分布 数据分片存储在多个主节点上,通过哈希槽(Slot,共16384个)分配数据。
高可用机制 1. 主从复制:Slave 实时同步 Master 数据
2. 故障转移:Master 宕机时,Slave 通过投票自动晋升为新的 Master。
故障检测 基于投票容错机制,超过半数节点认为某节点失效时触发故障转移(最少需3个主节点)。
扩展性 无中心化架构,支持水平扩展(官方建议不超过1000个节点)。
客户端访问 客户端可连接任意节点,节点内部通过重定向(MOVED/ASK)引导请求到正确分片。
优势 1. 高性能:无代理层,直接访问节点
2. 高可用:自动故障转移
3. 海量数据支持:分片存储突破单机限制。
适用场景 海量数据、高并发、高可用需求场景(如电商、社交平台等)。
配置复杂度 配置简单,但需预先规划分片和节点拓扑。

三:数据分片方式

特性 说明
分片基础 使用 CRC16 算法对 key 进行哈希,然后对 16384 取模确定 Slot 编号:HASH_SLOT = CRC16(key) % 16384
Slot 分配 1. 集群预分配 16384 个 Slot
2. 每个 Master 节点负责一部分 Slot
3. Slave 节点不分配 Slot,仅备份数据
请求路由 1. 客户端连接任意节点发送请求
2. 若请求的 key 不属于当前节点,返回 MOVED 重定向到正确节点
3. 客户端自动重发请求到目标节点
分片优势 1. 数据均匀分布,避免热点问题
2. 高并发:多节点并行处理
3. 故障隔离:单节点故障仅影响局部数据
分片类型 1. 客户端分片:由客户端计算 Slot 并直连节点(如 Jedis Cluster)
2. 代理分片:通过中间件(如 Twemproxy)路由请求
3. 混合分片:结合客户端与代理的优势
动态扩缩容 支持通过 CLUSTER ADDSLOTS 或工具(如 redis-trib)调整 Slot 分配,但需手动迁移数据
限制 1. 不支持跨 Slot 的多 Key 操作(如 MGET 需所有 key 在同一 Slot)
2. 事务仅限于单节点操作

1:客户端分片

         客户端分片方案是将分片工作放在业务程序端。程序代码根据预先设置的路由规则,直接对多个 Redis 实例进行分布式访问。这样的好处是,群集不依赖于第三方分布式中间件,实现方法和代码都自己掌控,可随时调整,不用担心踩到坑。这实际上是一种静态分片技术Redis 实例的增减,都得手工调整分片程序。基于此分片机制的开源产品,现在仍不多见。
         这种分片机制的性能比代理式更好(少了一个中间分发环节),但缺点是升级麻烦,对研发人员的个人依赖性强,需要有较强的程序开发能力做后盾。如果主力程序员离职,可能新的负责人会选择重写一遍。所以这种方式下可运维性较差,一旦出现故障,定位和解决都得研发人员和运维人员配合解决,故障时间变长。因此这种方案,难以进行标准化运维,不太适合中小公司。

2:代理分片

          代理分片方案是将分片工作交给专门的代理程序来做。代理程序接收到来自业务程序的数据请求,根据路由规则,将这些请求分发给正确的 Redis 实例并返回给业务程序。这种机制下,一般会选用第三方代理程序(而不是自己研发)。因为后端有多个 Redis 实例,所以这类程序又称为分布式中间件。这种分片机制的好处是业务程序不用关心后端 Redis实例,维护起来也方便。虽然会因此带来些性能损耗,但对于Redis 这种内存读写型应用相对而言是能容忍的。这是比较推荐的集群实现方案。像 Twemproxy、Codis 就是基于该机制的开源产品的其中代表,应用非常广泛。

特性 Twemproxy Codis
开发方 Twitter 开源 豌豆荚开源(Go/C 语言开发)
架构 单层代理,无中心节点 基于 Group 的主从架构(1 Master + N Slave)
分片机制 静态哈希分片,不支持动态调整 预分片 1024 Slot,支持动态迁移(Auto Rebalance)
扩容/缩容 不支持平滑扩缩容,需停机调整 支持在线数据迁移,扩容缩容更灵活
高可用性 依赖 Keepalived 实现代理层高可用 内置故障转移(通过 Dashboard 切换 Master/Slave)
运维支持 无控制面板,纯命令行操作 提供可视化 Dashboard,简化运维
性能损耗 约 20% 性能下降(相比直连 Redis) 优化后性能优于 Twemproxy
数据迁移 需手动迁移数据,复杂度高 支持热迁移(基于修改的 CodisServer)
依赖组件 无额外依赖 依赖 Zookeeper/Etcd 存储路由信息
适用场景 小规模静态集群,对性能要求不苛刻的场景 中大规模集群,需频繁扩缩容或高可用要求的场景

3:服务器端分片

       服务器端分片是 Redis 官方的集群分片技术。Redis-Cluster 将所有 Key 映射到16384个 slot 中,集群中每个 Redis 实例负责一部分,业务程序是通过集成的Redis-Cluster 客户端进行操作。客户端可以向任一实例发出请求,如果所需数据不在该实例中,则该实例引导客户端自动去对应实例读写数据。Redis-Cluster 成员之间的管理包括节点名称、IP、端口、状态、角色等,都通过节点与之间两两通讯,定期交换信息并更新。

四:负载均衡的实现

机制 实现方式 核心作用
客户端路由 1. 客户端通过 CRC16(key) % 16384 计算 Slot
2. 直连目标节点或根据 MOVED 重定向
避免代理层开销,直接分散请求到不同节点
自动迁移(Rebalance) 1. 节点增减时触发 Slot 迁移
2. 使用 CLUSTER ADDSLOTS 和 RESHARD 命令调整分布
动态扩缩容时保持数据均匀分布
故障检测与恢复 1. 节点间通过 Gossip 协议健康检测
2. 半数以上节点确认故障后触发从节点晋升
自动容灾,保障高可用性
分片+副本 1. 数据分片到多个主节点
2. 每个主节点配置从节点(副本)
1. 提升并发能力
2. 故障时无缝切换

与传统方案的对比

特性 Redis Cluster Twemproxy/Codis
负载均衡 客户端智能路由 + 自动迁移 依赖代理层静态分片
扩展性 无中心化,支持线性扩展 代理层可能成为瓶颈
故障恢复 内置 Gossip 协议和投票机制 需额外工具(如 Sentinel)

五:故障的处理

1:故障转移

步骤 触发条件 执行动作 集群状态变化
1 主节点被多数节点判定为 FAIL 从节点发起选举(基于配置纪元+运行ID) 集群进入故障转移准备阶段
2 从节点赢得选举 执行 SLAVEOF NO ONE 晋升为新主节点 原主节点槽位标记为「待迁移」
3 新主节点生效 1. 撤销原主节点槽位指派
2. 将槽位重新指派给自己
集群路由表更新
4 新主节点广播 发送 PONG 消息宣告主权 其他节点更新本地节点列表
5 客户端重定向 1. 客户端收到 MOVED 响应
2. 自动连接新主节点
业务请求恢复处理
6 槽位完整性检查 若集群配置 cluster-require-full-coverage=no 允许部分槽位缺失时继续提供服务 关键区别:
• yes(默认):必须16384个槽全部分配才可用
• no:容忍部分故障

故障转移耗时分析

阶段 耗时参考 影响因素
故障检测 1~15秒 cluster-node-timeout 参数设置
选举+晋升 1~2秒 从节点数量及网络延迟
槽位重新分配 瞬时完成 无数据迁移时仅元数据更新
全集群状态同步 1~5秒 集群规模及 Gossip 协议传播效率

2:多slave选举

步骤 触发条件 参与角色 关键动作 选举规则
1 主节点被判定为 FAIL 状态 所有从节点 广播 CLUSTERMSG_TYPE_FAILOVER_AUTH_REQUEST 请求投票 每个从节点独立发起选举
2 收到投票请求的主节点 具有投票权的主节点 检查是否已投票:
• 未投票 → 返回 CLUSTERMSG_TYPE_FAILOVER_AUTH_ACK
• 已投票 → 忽略请求
每个主节点仅能投一票(先到先得)
3 从节点收集投票 参与选举的从节点 统计收到的 ACK 消息数量 需获得 半数以上主节点+1 的票数(N/2+1)
4 票数达标 胜出从节点 执行 SLAVEOF NO ONE 晋升为新主节点 立即接管原主节点的 Slot 并广播 PONG 通知集群
5 票数不足(选举超时) 所有从节点 进入新一轮选举周期 随机延迟后重新发起投票(避免冲突)

六:Redis群集部署

资源列表

操作系统 主机名 配置 IP地址 角色 Slot分配建议 从属关系
OpenEuler24 master1 2C4G 192.168.10.101 Master 0-5460 对应 slave1
OpenEuler24 master2 2C4G 192.168.10.102 Master 5461-10922 对应 slave2
OpenEuler24 master3 2C4G 192.168.10.103 Master 10923-16383 对应 slave3
OpenEuler24 slave1 2C4G 192.168.10.104 Slave 不分配 Slot 复制 master1 数据
OpenEuler24 slave2 2C4G 192.168.10.105 Slave 不分配 Slot 复制 master2 数据
OpenEuler24 slave3 2C4G 192.168.10.106 Slave 不分配 Slot 复制 master3 数据

安装 redis(每个节点都要安装)

[root@localhost ~]# systemctl stop firewalld
[root@localhost ~]# setenforce 0
 
[root@localhost ~]# dnf -y install gcc* zlib-devel tar
[root@localhost ~]# tar xzvf redis-5.0.14.tar.gz
[root@localhost ~]# cd redis-5.0.14/
 
[root@localhost redis-5.0.14]# make
[root@localhost redis-5.0.14]# make PREFIX=/usr/local/redis install
[root@localhost ~]# ln -s /usr/local/redis/bin/* /usr/local/bin/
[root@localhost redis-5.0.14]# cd /root/redis-5.0.14/utils/
[root@localhost utils]# ./install_server.sh

修改配置文件(每个节点都要配置,只有 IP 地址不同,其他都相同)

[root@localhost ~]# vim /etc/redis/6379.conf
bind 0.0.0.0    ##70行
protected-mode yes
port 6379
tcp-backlog 511
timeout 0
tcp-keepalive 300
daemonize yes
supervised no
pidfile /var/run/redis_6379.pid
loglevel notice
logfile /var/log/redis_6379.log
appendonly yes    ##开启 aof 持久化,700行
cluster-enabled yes    ##83行,去掉注释符,表示启用群集
cluster-config-file nodes-6379.conf    ##841行,去掉注释
cluster-node-timeout 15000    ##847行,去掉注释
cluster-require-full-coverage no    ##924行,去掉注释,将yes改为no

注释:
开启 Cluster:cluster-enabled yes
集群配置文件:cluster-config-file nodes-7000.conf。这个配置文件不是要我们去配的,而是 Redis 运行时保存配置的文件,所以我们也不可以修改这个文件。
集群超时时间:cluster-node-timeout 15000。结点超时多久则认为它宕机了。
槽是否全覆盖:cluster-require-full-coverage no。默认是 yes,只要有结点宕机导致 16384 个槽没全被覆盖,整个集群就全部停止服务,所以一定要改为 no

[root@localhost ~]# /etc/init.d/redis_6379 restart
[root@localhost ~]# netstat -anpt | grep 6379
tcp        0      0 192.168.10.101:6379     0.0.0.0:*               LISTEN      20315/redis-server 
tcp        0      0 192.168.10.101:16379    0.0.0.0:*               LISTEN      20315/redis-server 

创建 redis 群集

任意一个节点

[root@localhost ~]# redis-cli --cluster create --cluster-replicas 1 192.168.10.101:6379 192.168.10.102:6379 192.168.10.103:6379 192.168.10.104:6379 192.168.10.105:6379 192.168.10.106:6379

备注:
需要至少 3 个 master 节点,副本数至少为 1,即每个 master 需要一个从节点

测试群集

[root@localhost ~]# redis-cli -h 192.168.10.106 -p 6379 -c
192.168.10.106:6379> set centos 7.3
 
192.168.10.106:6379> get centos
192.168.10.106:6379> quit
 
[root@localhost ~]# redis-cli -h 192.168.10.105 -p 6379 -c
192.168.10.105:6379> get centos
192.168.10.105:6379> quit

集群信息查看

[root@localhost ~]# redis-cli
192.168.10.106:6379> cluster nodes
 
[root@localhost ~]# redis-cli --cluster check 192.168.10.101:6379

添加节点

例如存在节点 192.168.10.107(并且已在配置文件中开启集群相关配置),把 192.168.10.107 加入现有集群

[root@localhost ~]# redis-cli -c -p 6379 cluster meet 192.168.10.107 6379
OK
[root@localhost ~]# redis-cli
127.0.0.1:6379> cluster nodes

注意:
添加完后此新节点是 master 的角色

添加节点的另一种方法

例如存在节点 192.168.10.108(并且已在配置文件中开启集群相关配置),把 192.168.10.108 加入现有集群

[root@localhost ~]# redis-cli --cluster add-node 192.168.10.108:6379 192.168.10.101:6379

注意:
10.108 是要新添加的节点,10.101 可以是当前集群中的任意一个节点

删除节点

清除 10.108 的槽位信息

[root@localhost ~]# redis-cli -h 192.168.10.108 -p 6379
192.168.10.109:6379> flushall
OK
192.168.10.109:6379> cluster reset
OK

删除10.108的节点

[root@localhost ~]# redis-cli --cluster del-node 192.168.10.108:6379 fe75330496c2c2af9c5a02b9819d6b2e8a4d8a2

注意:此ID为10.108的ID
如果删除的 slave 节点,直接删除即可,如果删除的是 master 节点,需要先清除槽信息再删除,并且建议清除 redis 实例中的集群配置文件并重启实例,例如 /var/lib/redis/6379/nodes-6379.conf

修改新节点为其他节点的从节点

[root@localhost ~]# redis-cli -h 192.168.10.108 -p 6379
192.168.10.108:6379> cluster replicate 5472bbc9226737ca2199e146080ddaa41686a694

注意:10.108 需要先加入到集群,再设置为其他 master 的 slave。

重新分配槽位
重新分片基本上意味着将 slot 重新分配,就像集群创建一样,它是使用 redis-cli 实用程序完成的。
平均分配所有的槽位,使用以下命令会自动将 16384 个槽位自动分配给集群的每一个 master,不用手动指定槽为分配。

[root@localhost ~]# redis-cli --cluster rebalance --cluster-threshold 1 --cluster-use-empty-masters 192.168.10.101:6379

只需要指定一个老的节点 (192.168.10.101:6379),redis 会自动查找其他节点。

如果某个 master 节点没有槽位,可以使用以下命令分槽:

[root@localhost ~]# redis-cli -h 127.0.0.1 -p 6379 CLUSTER ADDSLOTS {0..5460}

集群排错思路

/usr/local/rvm/gems/ruby-2.4.5/gems/redis-4.1.0/lib/redis/client.rb:124:in `call': ERR Slot 0 is already busy (Redis::CommandError)
        from /usr/local/rvm/gems/ruby-2.4.5/gems/redis-4.1.0/lib/redis.rb:3282:in `block in cluster'
        from /usr/local/rvm/gems/ruby-2.4.5/gems/redis-4.1.0/lib/redis.rb:50:in `block in synchronize'
        from /usr/local/rvm/rubies/ruby-2.4.5/lib/ruby/2.4.0/monitor.rb:214:in `mon_synchronize'
        from /usr/local/rvm/gems/ruby-2.4.5/gems/redis-4.1.0/lib/redis.rb:50:in `synchronize'
        from /usr/local/rvm/gems/ruby-2.4.5/gems/redis-4.1.0/lib/redis.rb:3281:in `cluster'
        from ./redis-trib.rb:212:in `flush_node_config'
        from ./redis-trib.rb:906:in `block in flush_nodes_config'
        from ./redis-trib.rb:905:in `each'
        from ./redis-trib.rb:905:in `flush_nodes_config'
        from ./redis-trib.rb:1426:in `create_cluster_cmd'
        from ./redis-trib.rb:1830:in `<main>'

注意 1:
在创建 redis 集群服务时,提示以下错误:

错误提示是说:slot 插槽被占用了、这是因为 搭建集群前时,以前 redis 的旧数据和配置信息没有清理干净。

注意 2:
如果配置文件中没有监听 127.0.0.1,在使用 /etc/init.d/redis_6379.conf start 启动 redis 服务时(停止或重启都是如此),会提示连接 127.0.0.1:6379 失败

解决方法:
使用如下方法启动

[root@localhost ~]# redis-server /etc/redis/6379.conf

使用如下命令关闭

[root@localhost ~]# redis-cli -h 192.168.10.103 -p 6379 shutdown

注意 3:
配置文件中如果没有监听 127.0.0.1 的地址,在连接是需要使用如下方法:

注意 3:
配置文件中如果没有监听 127.0.0.1 的地址,在连接是需要使用如下方法:

这里的 ip 为本机监听的 ip

注意 4:
在做群集的时候各个节点不要监听 127.0.0.1 的地址,否则,在建立群集时会有如下情况

>>> Creating cluster
>>> Performing hash slots allocation on 6 nodes...
Using 3 masters:
192.168.10.101:6379
192.168.10.102:6379
192.168.10.103:6379
Adding replica 192.168.10.104:6379 to 192.168.10.101:6379
Adding replica 192.168.10.105:6379 to 192.168.10.102:6379
Adding replica 192.168.10.106:6379 to 192.168.10.103:6379
M: 4d0690c0de9b90f0fda50a900c0502b4f33c35 192.168.10.101:6379
   slots:0-5460 (5461 slots) master
M: 9bcde42b8b5f3f1b0c228787c5776a01eaed2e1 192.168.10.102:6379
   slots:5461-10922 (5462 slots) master
M: 0f786b42a3e7ab0fd738b0d6618d0d1e1f4893 192.168.10.103:6379
   slots:10923-16383 (5461 slots) master
S: 61165389d4ba128f308ef8d83b2a6fc68e558 192.168.10.104:6379
   replicates 4d0690c0de9b90f0fda50a900c0502b4f33c35
S: efe896c745ece4001d8c8458b8a67a45a243a29 192.168.10.105:6379
   replicates 9bcde42b8b5f3f1b0c228787c5776a01eaed2e1
S: 418c2502edac8f9ce4b1540cf89ffbc0f01c 192.168.10.106:6379
   replicates 0f786b42a3e7ab0fd738b0d6618d0d1e1f4893
Can I set the above configuration? (type 'yes' to accept): 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........

系统会一直停留在这,这是在等待其他节点并到群集中,需要在其他 slave 的主机上接受其 master 才可以

Adding replica 192.168.10.104:6379 to 192.168.10.101:6379
Adding replica 192.168.10.105:6379 to 192.168.10.102:6379
Adding replica 192.168.10.106:6379 to 192.168.10.103:6379

按照这里的提示登录其他slave

127.0.0.1:6379> CLUSTER MEET 192.168.10.101 6379

然后等待连接即可

其他注意事项:

1.redis master 宕机,恢复后不会自动切为主
2.扩容 redis cluster 如果我们大量使用 redis cluster 的话,有一个痛点就是扩容的机器加入集群的时候,分配主从。现在只能使用命令去操作,非常的凌乱。而且如果 redis cluster 是线上集群或者是有数据的话,极其容易丢操作或数据。
3.redis cluster 从节点支持可读的前提下得在执行 readonly 执行,程序读取从节点时也需要执行。一次连接需要执行一次,客户端关闭即失效。
4.redis cluster 启动必须以绝对路径的方式启动,如果不用绝对路径启动,会产生新的 nodes.conf 文件,集群的主从对应关系就会变乱,从而导致集群奔溃。


网站公告

今日签到

点亮在社区的每一天
去签到