NoSQL之Redis集群
目录
一: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 计算 Slot2. 直连目标节点或根据 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.3192.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:6379slots:0-5460 (5461 slots) master
M: 9bcde42b8b5f3f1b0c228787c5776a01eaed2e1 192.168.10.102:6379slots:5461-10922 (5462 slots) master
M: 0f786b42a3e7ab0fd738b0d6618d0d1e1f4893 192.168.10.103:6379slots:10923-16383 (5461 slots) master
S: 61165389d4ba128f308ef8d83b2a6fc68e558 192.168.10.104:6379replicates 4d0690c0de9b90f0fda50a900c0502b4f33c35
S: efe896c745ece4001d8c8458b8a67a45a243a29 192.168.10.105:6379replicates 9bcde42b8b5f3f1b0c228787c5776a01eaed2e1
S: 418c2502edac8f9ce4b1540cf89ffbc0f01c 192.168.10.106:6379replicates 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 文件,集群的主从对应关系就会变乱,从而导致集群奔溃。