【redis】集群模式
Redis Cluster是Redis官方推出的分布式解决方案,旨在通过数据分片、高可用和动态扩展能力满足大规模数据存储与高并发访问的需求。其核心机制基于虚拟槽分区,将16384个哈希槽均匀分配给集群中的主节点,每个键通过CRC16哈希算法映射到特定槽位,实现数据的分布式存储与负载均衡。
集群模式
集群模式会自动对数据进行分区,存储到不同的节点上,集群通过分区来提供一定程度的可用性,当某个节点宕机或者不可达的情况下继续处理请求。
那么集群模式怎么对数据进行分片呢?使用哈希槽。
Redis集群有16384个哈希槽,每个key通过CRC16校验后对16384取模来决定放置哪个槽。集群的每个节点负责一部分hash槽,举个例子,比如当前集群有3个节点,那么:
-
节点A包含0到5460号哈希槽。
-
节点B包含5461到10922号哈希槽。
-
节点C包含10923到16383号哈希槽。
如果要添加节点D,可以从节点A, B, C中分出部分槽到D上,如果要移除节点A,可以将A中的槽移到B和C节点上,然后将没有任何槽的A节点从集群中移除即可。由于从一个节点将哈希槽移动到另一个节点并不会停止服务,所以无论添加删除或者改变某个节点的哈希槽的数量都不会造成集群不可用的状态。
另外集群模式中还使用了主从复制模型,每个master节点都要有1个或者多个slave,也就是上面具有A,B,C三个节点的集群中的每个节点还需要添加一个从节点,这样master节点挂了后,slave就会成为新的master继续提供服务,整个集群便不会因为key找不到对应的槽而不可用了。
集群的快速搭建
- 安装Redis
确保所有节点安装Redis 5.0+
版本(推荐最新稳定版),可通过源码编译或包管理器安装。
检查安装是否成功:redis-server --version
。
- 创建节点目录
$ mkdir /test && cd test$ mkdir 3001 3002 3003 4001 4002 4003 # 6个节点(3主3从)
- 生成配置文件
在每个端口目录(如3001
)下创建redis.conf
,内容如下:
port 3001
cluster-enabled yes
dir /test/3001
修改其他目录中的端口号,与目录名字保持一致。
关键配置说明
cluster-enabled
:启用集群模式。dir
:节点配置文件,持久化文件自动生成的路径
- 启动6个节点
$ redis-server /test/3001/redis.conf
$ redis-server /test/3002/redis.conf
$ redis-server /test/3003/redis.conf
$ redis-server /test/4001/redis.conf
$ redis-server /test/4002/redis.conf
$ redis-server /test/4003/redis.conf
如果需要后台运行可添加daemonize yes
到配置文件中,这里为了方便查询日志,选择前台运行redis。
- 创建集群
$ redis-cli --cluster create 127.0.0.1:3001 127.0.0.1:3002 127.0.0.1:3003 127.0.0.1:4001 127.0.0.1:4002 127.0.0.1:4003 --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:4002 to 127.0.0.1:3001
Adding replica 127.0.0.1:4003 to 127.0.0.1:3002
Adding replica 127.0.0.1:4001 to 127.0.0.1:3003
>>> Trying to optimize slaves allocation for anti-affinity
[WARNING] Some slaves are in the same host as their master
M: f011cb433549dbf90da9d6adc26b45d61bf02975 127.0.0.1:3001slots:[0-5460] (5461 slots) master
M: 10c949852b1c88e0902d7fca365319e579fff36c 127.0.0.1:3002slots:[5461-10922] (5462 slots) master
M: 7d4d863cd9d82817b9cbcad0475d03337ee2685f 127.0.0.1:3003slots:[10923-16383] (5461 slots) master
S: 092d615b4c56f67b2034ab67ffbb5d40d8e22a29 127.0.0.1:4001replicates 10c949852b1c88e0902d7fca365319e579fff36c
S: 64e6ac88c168664b1c2d9f5c621c8ff61d896e27 127.0.0.1:4002replicates 7d4d863cd9d82817b9cbcad0475d03337ee2685f
S: c3318bb75ee29a7326a502f818658e91af1bca35 127.0.0.1:4003replicates f011cb433549dbf90da9d6adc26b45d61bf02975
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
...
>>> Performing Cluster Check (using node 127.0.0.1:3001)
M: f011cb433549dbf90da9d6adc26b45d61bf02975 127.0.0.1:3001slots:[0-5460] (5461 slots) master1 additional replica(s)
S: c3318bb75ee29a7326a502f818658e91af1bca35 127.0.0.1:4003slots: (0 slots) slavereplicates f011cb433549dbf90da9d6adc26b45d61bf02975
M: 10c949852b1c88e0902d7fca365319e579fff36c 127.0.0.1:3002slots:[5461-10922] (5462 slots) master1 additional replica(s)
S: 092d615b4c56f67b2034ab67ffbb5d40d8e22a29 127.0.0.1:4001slots: (0 slots) slavereplicates 10c949852b1c88e0902d7fca365319e579fff36c
M: 7d4d863cd9d82817b9cbcad0475d03337ee2685f 127.0.0.1:3003slots:[10923-16383] (5461 slots) master1 additional replica(s)
S: 64e6ac88c168664b1c2d9f5c621c8ff61d896e27 127.0.0.1:4002slots: (0 slots) slavereplicates 7d4d863cd9d82817b9cbcad0475d03337ee2685f
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
--cluster-replicas 1
:表示副本数为1,即每个主节点分配1个从节点,集群会自动分配主从关系(前3节点为主,后3为从)。
集群的分步搭建
在Redis集群创建时,默认情况下主从节点是自动分配的,但可以通过先创建主节点再手动添加从节点实现精确控制主从关系。
- 先创建主节点集群
$ redis-cli --cluster create 127.0.0.1:3001 127.0.0.1:3002 127.0.0.1:3003 --cluster-replicas 0
>>> Performing hash slots allocation on 3 nodes...
Master[0] -> Slots 0 - 5460
Master[1] -> Slots 5461 - 10922
Master[2] -> Slots 10923 - 16383
M: 763624d529917f37adf9bdc1a07789dcf4af84e1 127.0.0.1:3001slots:[0-5460] (5461 slots) master
M: f2782f046fbc797f390b0dd4b3c8569aea91db24 127.0.0.1:3002slots:[5461-10922] (5462 slots) master
M: 07c53f427ad42fd51ec3517e92b6fefb430f8acf 127.0.0.1:3003slots:[10923-16383] (5461 slots) master
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
.
>>> Performing Cluster Check (using node 127.0.0.1:3001)
M: 763624d529917f37adf9bdc1a07789dcf4af84e1 127.0.0.1:3001slots:[0-5460] (5461 slots) master
M: 07c53f427ad42fd51ec3517e92b6fefb430f8acf 127.0.0.1:3003slots:[10923-16383] (5461 slots) master
M: f2782f046fbc797f390b0dd4b3c8569aea91db24 127.0.0.1:3002slots:[5461-10922] (5462 slots) master
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
关键参数:--cluster-replicas 0
表示没有副本,不分配从节点。
- 添加从节点并指定主节点
通过以下命令将新节点作为从节点加入,并绑定到特定主节点:
$ redis-cli --cluster add-node 127.0.0.1:4001 127.0.0.1:3001 --cluster-slave --cluster-master-id 763624d529917f37adf9bdc1a07789dcf4af84e1
>>> Adding node 127.0.0.1:4001 to cluster 127.0.0.1:3001
>>> Performing Cluster Check (using node 127.0.0.1:3001)
M: 763624d529917f37adf9bdc1a07789dcf4af84e1 127.0.0.1:3001slots:[0-5460] (5461 slots) master
M: 07c53f427ad42fd51ec3517e92b6fefb430f8acf 127.0.0.1:3003slots:[10923-16383] (5461 slots) master
M: f2782f046fbc797f390b0dd4b3c8569aea91db24 127.0.0.1:3002slots:[5461-10922] (5462 slots) master
[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:4001 to make it join the cluster.
Waiting for the cluster to join>>> Configure node as replica of 127.0.0.1:3001.
[OK] New node added correctly.
参数说明:
add-node 127.0.0.1:4001
:表示要添加进入集群的节点机器ip和端口127.0.0.1:3001
:表示要添加到哪个集群中,在集群中任意一个节点的ip和端口--cluster-slave
:表示要添加的节点为从节点。--cluster-master-id
:作为哪一个主节点的从节点,通过cluster nodes
命令获取主节点ID。
以同样的方式将127.0.0.1:4002
和127.0.0.1:4003
添加到集群中:
$ redis-cli --cluster add-node 127.0.0.1:4002 127.0.0.1:3001 --cluster-slave --cluster-master-id f2782f046fbc797f390b0dd4b3c8569aea91db24$ redis-cli --cluster add-node 127.0.0.1:4003 127.0.0.1:3001 --cluster-slave --cluster-master-id 07c53f427ad42fd51ec3517e92b6fefb430f8acf
就这样,集群模式搭建好了,大体架构如下:
验证集群状态
查看集群信息,连接集群中任意一个节点均可查看:
$ redis-cli -c -p 3001 cluster info
cluster_state:ok
cluster_slots_assigned:16384
cluster_slots_ok:16384
cluster_slots_pfail:0
cluster_slots_fail:0
cluster_known_nodes:6
cluster_size:3
cluster_current_epoch:3
cluster_my_epoch:1
cluster_stats_messages_ping_sent:170
cluster_stats_messages_pong_sent:175
cluster_stats_messages_sent:345
cluster_stats_messages_ping_received:170
cluster_stats_messages_pong_received:170
cluster_stats_messages_meet_received:5
cluster_stats_messages_received:345
查看集群节点信息:
$ redis-cli -c -p 3001 cluster nodes
4b1e423b6d7639124e402d438b6089d60189e1a0 127.0.0.1:4002@14002 slave f2782f046fbc797f390b0dd4b3c8569aea91db24 0 1746670611585 2 connected
406face68543d8aca8addd441ed49c3546c43748 127.0.0.1:4003@14003 slave 07c53f427ad42fd51ec3517e92b6fefb430f8acf 0 1746670612588 3 connected
f2782f046fbc797f390b0dd4b3c8569aea91db24 127.0.0.1:3002@13002 master - 0 1746670610582 2 connected 5461-10922
07c53f427ad42fd51ec3517e92b6fefb430f8acf 127.0.0.1:3003@13003 master - 0 1746670609579 3 connected 10923-16383
763624d529917f37adf9bdc1a07789dcf4af84e1 127.0.0.1:3001@13001 myself,master - 0 1746670611000 1 connected 0-5460
f00ce14af6b862d3633649f4702477e0f005399c 127.0.0.1:4001@14001 slave 763624d529917f37adf9bdc1a07789dcf4af84e1 0 1746670607573 1 connected
集群数据的访问
请求重定向(MOVED)
触发场景:当客户端请求的键不属于当前连接节点管理的槽时,节点返回MOVED <slot> <ip:port>
错误,指示客户端永久重定向到正确节点。
$ redis-cli -h 127.0.0.1 -p 3001
127.0.0.1:3001> set a b
(error) MOVED 15495 127.0.0.1:3003
普通客户端(Dummy):需手动处理MOVED错误,重新发送请求到新节点,可能导致额外IO开销。
$ redis-cli -c -h 127.0.0.1 -p 3001
127.0.0.1:3001> set a b
-> Redirected to slot [15495] located at 127.0.0.1:3003
OK
127.0.0.1:3003>
需要使用-c
选项请求才会自动重定向。
Smart客户端:自动更新本地槽映射缓存,后续请求直接发往正确节点,减少重定向次数。
ASK重定向
触发场景:在槽迁移过程中,若键已迁移到目标节点但槽未完全移交,源节点返回ASK <slot> <ip:port>
错误,提示客户端临时重定向。
客户端处理:
- 向目标节点发送
ASKING
命令,临时绕过槽归属校验。 - 重新执行原命令。此重定向为临时性,客户端不更新本地槽映射缓存。
CALL命令
CALL
命令用于在集群所有节点上执行相同命令(如监控或批量操作),例如:
redis-cli -c -h 127.0.0.1 -p 3001 get a
此命令通过连接任一节点广播操作,适用于跨节点批量查询或管理任务。
Smart客户端优化
核心机制:
- 本地缓存槽映射:启动时通过
CLUSTER SLOTS
初始化槽-节点关系,减少重定向查询。 - 自动错误处理:捕获MOVED/ASK错误,动态更新缓存并重试请求,提升效率。
优势:
- 减少网络开销:避免每次请求依赖重定向,降低延迟。
- 支持高级操作:对批量命令(如MGET)自动拆分请求并按节点分组执行,兼容事务和Lua脚本(需使用hashtag确保键在同一节点)。
$ redis-cli -c -h 127.0.0.1 -p 3001 cluster slots
1) 1) (integer) 02) (integer) 54603) 1) "127.0.0.1"2) (integer) 30013) "763624d529917f37adf9bdc1a07789dcf4af84e1"4) 1) "127.0.0.1"2) (integer) 40013) "f00ce14af6b862d3633649f4702477e0f005399c"
2) 1) (integer) 54612) (integer) 109223) 1) "127.0.0.1"2) (integer) 30023) "f2782f046fbc797f390b0dd4b3c8569aea91db24"4) 1) "127.0.0.1"2) (integer) 40023) "4b1e423b6d7639124e402d438b6089d60189e1a0"
3) 1) (integer) 109232) (integer) 163833) 1) "127.0.0.1"2) (integer) 30033) "07c53f427ad42fd51ec3517e92b6fefb430f8acf"4) 1) "127.0.0.1"2) (integer) 40033) "406face68543d8aca8addd441ed49c3546c43748"
集群的扩容
-
准备新节点
部署2个新的Redis实例3004和4004,与前面redis的配置一直,并启动。 -
加入集群
$ redis-cli --cluster add-node 127.0.0.1:3004 127.0.0.1:3001
新节点默认为主节点,但无槽分配。
- 槽迁移
$ redis-cli --cluster reshard 127.0.0.1 3001
>>> Performing Cluster Check (using node 127.0.0.1:3001)
M: 763624d529917f37adf9bdc1a07789dcf4af84e1 127.0.0.1:3001slots:[0-5460] (5461 slots) master1 additional replica(s)
S: 4b1e423b6d7639124e402d438b6089d60189e1a0 127.0.0.1:4002slots: (0 slots) slavereplicates f2782f046fbc797f390b0dd4b3c8569aea91db24
M: c18809be8dea40cd1f3afb054dcedcf555dceee8 127.0.0.1:3004slots: (0 slots) master
S: 406face68543d8aca8addd441ed49c3546c43748 127.0.0.1:4003slots: (0 slots) slavereplicates 763624d529917f37adf9bdc1a07789dcf4af84e1
M: f2782f046fbc797f390b0dd4b3c8569aea91db24 127.0.0.1:3002slots:[5461-10922] (5462 slots) master1 additional replica(s)
M: 07c53f427ad42fd51ec3517e92b6fefb430f8acf 127.0.0.1:3003slots:[10923-16383] (5461 slots) master1 additional replica(s)
S: f00ce14af6b862d3633649f4702477e0f005399c 127.0.0.1:4001slots: (0 slots) slavereplicates 07c53f427ad42fd51ec3517e92b6fefb430f8acf
[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)? 4096
What is the receiving node ID? c18809be8dea40cd1f3afb054dcedcf555dceee8
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: 763624d529917f37adf9bdc1a07789dcf4af84e1
Source node #2: f2782f046fbc797f390b0dd4b3c8569aea91db24
Source node #3: 07c53f427ad42fd51ec3517e92b6fefb430f8acf
Source node #4: done
。。。。。。
交互式输入迁移槽数量、目标节点ID、源节点。
- 调整主从关系
为新主节点添加从节点,提升可用性:
$ redis-cli --cluster add-node 127.0.0.1:4004 127.0.0.1:3001 --cluster-slave --cluster-master-id c18809be8dea40cd1f3afb054dcedcf555dceee8
- 验证状态
redis-cli --cluster check 127.0.0.1:3001
确认槽分布均衡且集群状态正常。
集群的缩容
缩容就是对扩容进行逆向操作。
- 删除从节点:
$ redis-cli --cluster del-node 127.0.0.1:3001 a272e966f049c828af0e2ef6aecee521aab1bdc6
>>> Removing node a272e966f049c828af0e2ef6aecee521aab1bdc6 from cluster 127.0.0.1:3001
>>> Sending CLUSTER FORGET messages to the cluster...
>>> Sending CLUSTER RESET SOFT to the deleted node.
- 迁移主节点槽位:
将3004节点的0-1364槽迁移到3001:
$ redis-cli --cluster reshard 127.0.0.1:3001
。。。
How many slots do you want to move (from 1 to 16384)? 1365
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: c18809be8dea40cd1f3afb054dcedcf555dceee8
Source node #2: done
。。。
将3004节点的5461-6826槽迁移到3002:
$ redis-cli --cluster reshard 127.0.0.1:3001
。。。
How many slots do you want to move (from 1 to 16384)? 1366
What is the receiving node ID? f2782f046fbc797f390b0dd4b3c8569aea91db24
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: c18809be8dea40cd1f3afb054dcedcf555dceee8
Source node #2: done
。。。
将3004节点的10923-12287槽迁移到3003:
$ redis-cli --cluster reshard 127.0.0.1:3001
。。。
How many slots do you want to move (from 1 to 16384)? 1365
What is the receiving node ID? 07c53f427ad42fd51ec3517e92b6fefb430f8acf
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: c18809be8dea40cd1f3afb054dcedcf555dceee8
Source node #2: done
。。。
- 删除主节点
$ redis-cli --cluster del-node 127.0.0.1:3001 c18809be8dea40cd1f3afb054dcedcf555dceee8
>>> Removing node c18809be8dea40cd1f3afb054dcedcf555dceee8 from cluster 127.0.0.1:3001
>>> Sending CLUSTER FORGET messages to the cluster...
>>> Sending CLUSTER RESET SOFT to the deleted node.
平衡(rebalance)slot
若节点间槽数差异超过总槽数的5%(约820槽),需触发平衡
redis-cli --cluster rebalance 10.0.0.1:6379 \--cluster-threshold 1 \ # 允许1%的槽位偏差--cluster-use-empty-masters # 包含空节点
流程说明:
- 权重计算:根据节点内存、CPU、网络带宽动态分配权重
- 迁移规划:自动生成从高负载节点到低负载节点的迁移路径
- 分批次执行:默认每次迁移100个槽,可通过–cluster-pipeline调整批次大小
键哈希标签
由于集群模式中对数据进行了分片,对key进行聚合操作以及事务等都很难实现,如果想要某些key落在同一个redis实例中,该怎么实现呢?键哈希标签。
键哈希标签(Keys hash tags):可以确保带有相同标签的键都会在同一个哈希槽。
127.0.0.1:3001> set {xxx}name morris
OK127.0.0.1:3001> set {xxx}age 18
OK127.0.0.1:3001> keys *
1) "{xxx}age"
2) "{xxx}name"