Redis集群模式详解:从哈希槽原理到高可用架构实践

发布时间:2026/9/8 14:41:43
Redis集群模式详解:从哈希槽原理到高可用架构实践 1. 为什么需要集群模式单机、主从、哨兵的边界到底在哪先聊点实在的。很多人一开始用 Redis图的就是单机部署简单、性能强悍几行配置就能把缓存跑起来。但只要你做过线上业务迟早会遇到几个绕不开的坎单机内存不够了怎么办并发量上来了单节点扛不住怎么办节点挂了一票业务直接报错怎么保证高可用单机 Redis 的性能天花板其实非常高一台物理机跑个 10 万 QPS 问题不大但瓶颈从来不只是 CPU内存容量、网络带宽、持久化时的 fork 开销都会拖后腿。更关键的是单机模式下一切就依赖于那一台机器磁盘坏了、机房断电、系统崩溃整个缓存层就全完了。于是大家开始做“主从复制”一主多从主节点负责写从节点负责读相当于把读压力分摊出去。主从架构解决了部分读扩展问题但主节点写挂了从节点并不会自动顶上业务照样断。所以又有了哨兵模式哨兵负责监控主节点状态挂了自动把某个从节点提升为主节点。哨兵架构做到这一步已经从“单点故障”升级到了“高可用”但有两个核心问题依旧没法解决第一主从和哨兵模式下所有节点都保存全量数据数据总量受限于单机内存没法横向扩展第二主节点仍然只有单点写入写能力的扩展无从谈起。Redis 集群模式就是把这两件事一起解决。官方在 Redis 3.0 版本正式引入了 Cluster 集群方案核心思路是“分片 复制 自动故障转移”数据自动拆分到多个主节点上每个主节点带若干从节点做备份节点之间通过 Gossip 协议互相通信任何主节点挂了它下面的从节点会自动顶上。这套方案让 Redis 真正具备了水平扩展能力和高可用能力既能解决数据量超过单机内存的问题也能解决写入吞吐量需要横向扩容的问题。如果你的数据量在 10G 以内单机加哨兵完全够用一旦数据量增长到几十 G 甚至百 G 级别写入 QPS 又持续走高那就该认真考虑集群模式了。2. 集群架构的核心机制16384 个槽位是理解一切的钥匙2.1 数据分片的基础Hash Slot 哈希槽Redis 集群并没有采用一致性哈希而是设计了固定 16384 个哈希槽Hash Slot的机制。整个集群的数据空间被划分成 16384 个槽位每个 key 通过 CRC16 算法计算出一个 16 位的哈希值再对 16384 取模得到 0 到 16383 之间的槽位编号。slot CRC16(key) % 16384这 16384 个槽位会被均匀分配给集群里的各个主节点。比如 3 个主节点的集群每个节点大约负责 5461 个槽位。写数据时客户端计算 key 对应的槽位再定位到负责该槽位的节点直接发给那个节点执行。这套设计最大的好处是数据分布与物理节点解耦。扩容时只需要把一部分槽位从旧节点迁移到新节点而不是像一致性哈希那样涉及大量数据的重分布迁移粒度可以精确到槽位级别运维成本可控得多。2.2 每个节点都不是单兵作战主从复制是标配集群模式下数据分片实现了“总数据量变大”的目标但每个分片节点依然是单点。比如 3 个主节点各负责 1/3 的数据其中一台宕机了对应分片的数据就会全部不可用。所以集群模式规定每个主节点至少要配置一个从节点。主节点负责读写请求从节点实时同步主节点数据一旦主节点宕机从节点会通过集群内部的投票机制自动晋升为新的主节点整个过程对外基本无感知。生产环境建议每个主节点至少挂 1 个从节点重要业务甚至挂两个避免一个从节点同时宕机的极端情况。2.3 集群内部通信机制Gossip 协议和 Cluster Bus集群节点之间会开启一个额外的端口默认是客户端端口加 10000。比如 Redis 服务端口是 6379那么集群总线端口就是 16379。节点之间通过这个总线端口使用二进制协议进行通信传播的消息包括节点状态、槽位信息、故障信息等。Gossip 协议是节点间通信的核心。每个节点会周期性向其他节点发送 Ping 消息收到 Ping 的节点会回复 Pong。通过这种“病毒式传播”的机制集群中任何一个节点的状态变化都会在几秒内扩散到所有节点。这种设计不用全连接节点数量多时也能保持较好的扩展性官方建议集群节点数不要超过 1000 个再往上 Gossip 通信的带宽开销就会比较明显。故障检测分为两个阶段主观下线PFAIL和客观下线FAIL。当一个节点长时间联系不上某个节点时会将其标记为主观下线然后把这个信息通过 Gossip 传播出去。当集群中超过一半的主节点都认为某个节点主观下线时该节点会被标记为客观下线触发故障转移流程。这套机制对应到日常运维中有一个非常直观的体现检查集群状态时不要只盯着某个节点的日志要多看几个节点的视角。单节点认为某个机器故障可能只是网络抖动多个节点同时认为故障那才是真正的宕机。2.4 集群的自动故障转移是怎么完成的当某个主节点被标记为客观下线后它的从节点会发起竞选。从节点会检查自己复制主节点的数据偏移量数据越新的从节点竞选优先级越高。竞选成功后从节点执行 slaveof no one 把自己升级为主节点并广播槽位信息给整个集群告诉所有人“这些槽位现在归我管”。整个故障转移过程通常在几秒到十几秒内完成期间依赖该分片的请求会收到 CLUSTERDOWN 或 MOVED 重定向错误。客户端如果实现了集群协议会自动感知槽位变化并更新本地路由缓存业务则基本无感知。这里有一个非常关键的细节Redis 集群要求至少要有 3 个主节点才能正常工作这其实是投票机制决定的。因为客观下线判断需要多数节点同意如果只有 2 个主节点一个挂了只剩另一个永远无法凑成“多数”故障转移就永远触发不了。这也是搭建集群时最低要求 3 主 3 从的原因之一。3. 从零搭建 Redis 集群完整实操流程3.1 环境准备和版本选择先强调一个版本问题Redis 5.0 之前搭建集群用的是 redis-trib.rb 脚本依赖 Ruby 环境安装过程麻烦得一塌糊涂。Redis 5.0 之后官方把所有集群管理命令都集成到了 redis-cli 里不需要再装任何额外依赖版本直接选 6.x 或 7.x 就行。本文实操以 Redis 7.0 为例操作系统是 Linux。规划一个最基础的 3 主 3 从集群6 个节点的端口分配如下节点IP端口角色node-1192.168.31.1017001masternode-2192.168.31.1027002masternode-3192.168.31.1037003masternode-4192.168.31.1047004slavenode-5192.168.31.1057005slavenode-6192.168.31.1067006slave先完成基础安装源码编译安装步骤比较简单官方包下载后按标准流程执行即可wget https://download.redis.io/releases/redis-7.0.14.tar.gz tar xzf redis-7.0.14.tar.gz cd redis-7.0.14 make -j 4 make install执行完 make install 后redis-server 和 redis-cli 会被安装到 /usr/local/bin 目录下。到这里基础环境就准备好了。3.2 多实例配置每个节点都要独立配置每条配置项背后都很实用。用端口号区分目录方便归档for port in 7001 7002 7003 7004 7005 7006 do mkdir -p /data/redis-cluster/${port} done每个节点需要一份独立的 redis.conf 配置文件核心配置项如下port 7001 daemonize yes dir /data/redis-cluster/7001 pidfile /var/run/redis_7001.pid logfile /data/redis-cluster/7001/redis.log cluster-enabled yes cluster-config-file nodes-7001.conf cluster-node-timeout 15000 appendonly yes protected-mode no bind 0.0.0.0cluster-enabled yes 表示开启集群模式这是最核心的一项cluster-config-file 是集群节点状态文件由 Redis 自动维护记录了节点的 ID、槽位分配、主从关系等信息不要手工修改cluster-node-timeout 是节点超时时间单位毫秒超过这个时间联系不上就判定为主观下线一般设置 10 到 30 秒protected-mode no 是让集群节点之间可以跨 IP 互相访问生产环境建议通过防火墙限制来源 IP而不是依赖这个配置。把配置复制到对应目录逐个启动redis-server /data/redis-cluster/7001/redis.conf redis-server /data/redis-cluster/7002/redis.conf # 依次启动全部 6 个节点启动后检查进程状态用 redis-cli -p 端口 ping所有节点能正常响应即可。3.3 创建集群一条命令完成初始化Redis 5.0 之后创建集群变得非常简单redis-cli --cluster create \ 192.168.31.101:7001 \ 192.168.31.102:7002 \ 192.168.31.103:7003 \ 192.168.31.104:7004 \ 192.168.31.105:7005 \ 192.168.31.106:7006 \ --cluster-replicas 1--cluster-replicas 1 表示每个主节点配 1 个从节点。执行后 redis-cli 会自动把前 3 个节点配置为主节点后 3 个节点自动分配为从节点并打印出槽位分配规划询问你是否继续输入 yes 确认执行。这里需要注意一个细节自动分配方案中从节点的位置是算法根据节点 IP 和端口计算出来的不保证主从一定在不同物理机。生产环境建议使用手动方式先创建 3 个主节点再用 cluster replicate 命令手动指定从节点这样可以把主从分布在不同机架和物理机上。执行完成后可以用 cluster info 查看状态redis-cli -c -p 7001 cluster info如果输出 cluster_state:ok说明集群已经正常运行。3.4 验证集群状态槽位分配是否合理先看节点信息和槽位分配redis-cli -p 7001 cluster nodes正常输出应该包含 6 个节点每行信息包括节点 ID、IP:端口、角色标志master 或 slaveslave 会带 failover 相关的标志、槽位范围等。3 个主节点的槽位应该是均匀分配的0-5460 5461-10922 10923-16383再实际写入数据验证一下。Redis 集群模式下客户端需要以集群模式连接。如果直接用普通模式连接某个节点写入可能会遇到 MOVED 错误redis-cli -p 7001 set name zhang (error) MOVED 12933 192.168.31.103:7003这不是报错而是集群协议的正常行为key 经过哈希计算后槽位不在当前节点服务端会告诉客户端应该去哪个节点操作。如果用 -c 参数以集群模式连接redis-cli 会自动处理跳转redis-cli -c -p 7001 set name zhang OK到这里一个最基础但可用的 Redis 集群已经搭建完成。4. 集群模式下的工程细节写代码之前必须搞懂的坑4.1 多键操作的限制与 Hash Tag 的用法集群模式下不同 key 的槽位可能落在不同节点上所以涉及到多个 key 的操作统统受到限制。比如传统的 MSET、MGET、事务 MULTI/EXEC、Lua 脚本如果操作多个 key这些 key 的槽位必须一致否则就会报 CROSSSLOT 错误(error) CROSSSLOT Keys in request dont hash to the same slot业务要兼容集群模式就得把需要批量操作的 key 设计成同一个槽位。Redis 提供了 Hash Tag 机制来实现这一点当 key 中含有花括号 {} 时只会对大括号内的内容计算哈希。user:10001:profile # 没有 hash tag整体参与哈希运算 {user:10001}:profile # 只看 {} 内的内容即 user:10001 {user:10001}:orders # 与上面这个 key 一定在同一个槽位这意味着如果你有“同一个用户的多条数据需要一起查、一起更新”的场景把用户 ID 作为 hash tag就能让所有相关 key 落在同一个节点、同一个槽位上批量操作就可以正常执行了。注意 hash tag 使用要克制如果所有 key 都加了同一个 hash tag所有数据都会挤到同一个槽位上集群数据分布就废了。4.2 分布式锁在集群模式下的一致性坑网上大量教程里讲的 Redis 分布式锁实现——SET key value NX EX 10 这样一套逻辑默认都是单机 Redis。这套逻辑在哨兵模式下已经存在隐患了主节点写入锁成功后还没同步到从节点主节点就挂了从节点顶上后锁就丢了。集群模式下这个隐患同样存在而且因为分片原因锁 key 只存在于其中一个分片故障影响范围更集中。官方给出的标准答案是 Redlock 算法在多个完全独立的 Redis 节点上依次加锁只有超过半数节点加锁成功才算获取锁成功。Redlock 的实现复杂度不低要处理时钟漂移、重试退避、锁续期一堆问题很多团队直接用 Redisson 的 RLock 实现。Redisson 内部对 Redlock 做了封装还带了看门狗自动续期机制。如果你的系统处于并发量不高、业务幂等性好的场景也可以不用分布式锁用数据库唯一约束、版本号乐观锁等方式实现互斥省掉这一层的复杂度。反过来如果一定要用锁生产环境建议优先 Redisson不要自己造轮子分布式锁涉及一致性问题真不是简单几条命令能搞定的。4.3 数据倾斜和热点 Key 问题集群模式解决了容量扩展问题但槽位是均匀分的数据是否均匀就不一定了。某些 key 的数据量特别大或者某些 key 的访问频率特别高都会导致单个分片节点的负载远高于其他节点出现“木桶效应”。热点 key 通常有两种表现一是内存倾斜某个哈希槽内的数据特别大二是在单位时间内被超高并发读取单个节点的网卡或 CPU 成为瓶颈。针对内存倾斜可以把大 key 拆分比如把一个大 Hash 拆成多个小 Hash每个 key 加一个随机后缀分布在不同的槽位上。针对高并发读热点可以在客户端加一层本地缓存用 Caffeine 之类的本地缓存框架挡掉一部分流量或者给热点 key 加随机后缀分散读取压力。这里有个反直觉的点平时被强调的“大量 key 用同一个 hash tag”在某些业务里反而是制造热点的方式。比如把同一个用户的所有 key 都放在一个分片上确实方便了逻辑但这个用户如果是个头部大 V流量暴涨单个分片就跟着遭殃。设计时宁可在一致性上费点劲也别把鸡蛋全放一个篮子里。4.4 批量操作替代方案和 Pipeline 使用要点集群模式下执行不了原生的 MGET/MSET常见的代替方案是客户端循环获取。但循环单条获取在网络开销上比较吃亏应该用 Pipeline 把多条命令一次性发给客户端所连接的节点。需要注意一个关键细节Pipeline 通常解决的是“单个节点上的批量命令”问题。如果 MGET 的 key 分散在不同槽位、不同节点上使用连接某个固定节点的 Pipeline 只能批量获取该节点上的 key其他节点上的 key 还是要单独请求。所以集群客户端在做 Pipeline 时一般会先按槽位把 key 分组每个组分别和目标节点建立 Pipeline 传输。很多客户端库已经封装了这个逻辑比如 Lettuce 的集群模式下就支持自动分组路由。业务侧的缓存架构也要提前设计。排查问题时常见思路是查询慢的接口找到其 DB 访问路径拆分为“本地缓存 Redis 集群缓存 DB 三级结构”本地缓存解决热点高频访问Redis 集群缓存解决跨实例共享和分布式场景DB 兜底保证一致性。但加了一层缓存就要多想一层失效策略。集群模式下的缓存一致性比单机复杂因为数据分散在多个节点上批量删除 key 时会跨节点执行延迟可能拉长所以过期时间设计要留出余量不要追求极端的一致性Base 理论在这里远比 ACID 实用。5. 集群运维从扩容到缩容的完整操作实录5.1 集群扩容从 3 主到 4 主槽位迁移怎么做新节点加入集群后默认不会自动分担数据需要手动把槽位迁移过去。先把新节点加到集群里redis-cli --cluster add-node 192.168.31.107:7007 192.168.31.101:7001add-node 命令格式是 redis-cli --cluster add-node 新节点地址 任意一个已存在节点的地址。执行完之后可以用 cluster nodes 确认新节点已经加入。新节点加入后是 master 角色但槽位为空也没有数据。接下来执行槽位重新分配redis-cli --cluster reshard 192.168.31.101:7001这个命令是交互式的会问你要迁移多少个槽位、把槽位迁移到哪个节点 ID、从哪些节点迁出可以指定节点 ID也可以输入 all 让系统自动从所有节点均匀抽取。要迁移的槽位数量根据集群规模估算如果当前集群总计 16384 个槽位、有 3 个主节点新加 1 个主节点后想让 4 个节点均匀分布每个节点应该是 4096 个槽位所以要迁移 5461 - 4096 1365 个槽位给新节点。迁移过程中涉及到的 key 会逐个从源节点移动到目标节点整个过程中 key 的读写不会中断。查询时如果 key 还没迁走源节点会正常处理同时返回一个 ASK 错误告知客户端“这个 key 正在被迁移到新节点”客户端智能处理后继续访问源节点直到完成迁移。这是集群模式下数据迁移平滑执行的关键机制。5.2 集群缩容先把槽位搬空再摘除节点缩容的步骤刚好相反先把要下线的节点上的所有槽位迁移出去让节点变成“空壳”再把它从集群中移除。redis-cli --cluster reshard 192.168.31.101:7001执行 reshard 后把要下线的节点的所有槽位均匀迁移到现有节点比如原来有 4 个主节点目标是缩到 3 个被下线节点的 4096 个槽位可以按 1365、1365、1366 的比例分配给剩余 3 个节点。槽位搬空后执行下线命令redis-cli --cluster del-node 192.168.31.101:7001 3fc4e...节点IDdel-node 参数是集群中任一存活节点的地址和要删除的节点 ID。如果删除的是从节点整个流程更简单没有槽位需要迁移直接删就行。生产环境缩容场景往往不是主动规划而是集群节点触发“下线维护”。比如某台物理机硬件老化需要退役正确做法是先把该机器上的从节点逐个删除再把主节点的槽位迁移到其他节点最后让该机器上的所有 Redis 进程退出整个过程业务无感知。5.3 常见集群故障和处理节点宕机、槽位不一致、脑裂生产环境最常见的集群故障就是节点宕机。如果是某个主节点的从节点宕机由于主节点还在工作集群状态不受影响处理方式就是直接重启该从节点重启后它会自动从主节点同步数据并重新加入集群。如果是某个主节点宕机且它配置了从节点集群会自动触发故障转移。此时应登录到从节点所在机器用 cluster nodes 确认新主节点是否已经选举成功集群状态是否恢复为 ok。如果主节点没有从节点那就是最坏情况该分片数据不可用集群会处于 fail 状态。此时只能尽快恢复原主节点进程Redis 启动后会自动发现自己是旧主节点并降级为从节点同步新主节点数据。槽位不一致是另一个常见故障现象。正常情况下任意节点执行 cluster slots 命令返回的槽位分布应该完全一致。外部运维脚本可以用定时任务定期检查所有节点的槽位配置如果发现某个节点和其他节点的槽位信息不一致先排查是网络分区还是节点状态异常再用 cluster setslot 相关命令修复。所谓“脑裂”本质是网络分区导致集群中出现两个“主节点”各自为政短时间内客户端可能写入到旧主节点网络恢复后旧主节点发现自己的纪元低于新主节点会回滚数据并变成从节点。Redis 集群的故障转移机制里有节点超时时间和投票机制兜底出现脑裂时间窗口非常短但要想完全避免脑裂造成的数据丢失核心手段是控制 min-replicas-to-write 参数让主节点在从节点数量不足时拒绝写入。5.4 集群监控与性能优化日志、慢查询、内存治理集群节点日志默认输出在配置文件里 logfile 指定的位置。正常运行时日志量不大一旦发生节点状态切换、故障转移、槽位迁移等事件日志会记录关键信息。日常巡检建议重点看日志里这几个关键字fail、restart、epoch、slot这些对应了节点状态变化和槽位迁移记录。慢查询日志也是一个重要的分析入口。通过 SLOWLOG GET 命令获取超过指定耗时的操作记录定位哪些 key 的命令耗时异常。常见的慢查询原因有三种一是 big key 操作耗时高比如对一个包含几十万元素的 List 做 LRANGE 全量读取二是 KEYS 命令或全量 SCAN 在高负载下拖垮节点三是复杂的 Lua 脚本长时间阻塞节点。集群模式下建议统一禁用 KEYS 命令用好 SCAN 系列命令做渐进式遍历。内存治理的核心思路是定位大 key。集群模式的每个分片节点上可以执行redis-cli -p 7001 --bigkeys这个命令会自动扫描节点上的所有 key按类型统计最大的 key 有哪些。大数据量下建议用 --scan 加 --bigkeys 的参数组合或者直接用 redis-cli --memkeys 看内存占用。发现大 key 后Hash、Set、Zset 类型的大 key 可以拆分String 类型的大 key 需要确认是否应当改用其他存储。整个集群维度可以通过 redis-cli --cluster info 查看每个节点的内存占用和 key 数量分布用来评判数据分布是否均匀。6. 客户端选型与连接集群的实现方式服务端集群搭起来了客户端怎么连现在主流语言都有非常成熟的 Redis 集群客户端比如 Java 生态的 Lettuce、JedisGo 生态的 go-redisPython 生态的 redis-py。这些客户端大多实现了集群协议自动处理路由和重定向逻辑。以 Java 的 Spring Boot 项目为例现在官方推荐使用的是 Lettuce。配置方式比较简单spring: data: redis: cluster: nodes: - 192.168.31.101:7001 - 192.168.31.102:7002 - 192.168.31.103:7003 lettuce: pool: max-active: 50 max-idle: 20只需要在配置里填几个种子节点地址Lettuce 启动时会自动从这些节点拉取完整的集群拓扑感知所有节点的槽位分布。后续任何节点的状态变化、槽位迁移、主从切换Lettuce 都能通过订阅集群事件动态更新拓扑不需要重启应用。如果项目里用的是 Jedis 2.x 版本需要升级到 3.x 以上才支持集群模式。Jedis 的集群客户端是 JedisCluster使用方式相对底层一些。go-redis 的集群模式则是 UniversalClient 的实现配置和普通客户端非常接近只需要设置 Addrs 为多个集群节点地址并开启 ClusterMode 相关选项。使用客户端过程中遇到过的最常见报错有两类。一类是连接漂移问题节点挂了客户端还在向旧节点发请求收到连接拒绝或超时表现为接口短时间抖动排查方法就是确认客户端的拓扑刷新机制是否正常另一类是“max redirects exceeded”客户端在请求某个 key 时反复被重定向通常是因为集群处于迁移或故障处理过程中拓扑信息不稳定遇到这种情况先检查集群状态不要急着改代码。7. 面试和方案答辩视角的集群模式问题梳理集群模式也是 Redis 面试里的高频考点。结合这些问题能更清楚地看到集群模式设计上的取舍和完善之处。先看“为什么 Redis 集群槽位数量是 16384 而不是更多”这个问题。官方设计文档里给出的解释主要有几点一是网络带宽节点之间通过 Gossip 协议传播心跳信息时需要携带节点的槽位 bitmap16384 个槽位对应 2KB 的 bitmap如果扩展到 65536 个槽位心跳包会明显变大集群早期节点数量多时带宽开销会很可观二是集群规模上限官方推荐节点数不超过 1000 个16384 个槽位对于 1000 个节点来说均分状态也足够用了三是 CRC16 算法的特性16384 这个数值是经过实际测试验证的能较好地保证 key 的分布均匀性。再看“主从切换和哨兵模式的区别”这个问题。哨兵模式本质上是整个 Redis 实例层面的高可用方案所有节点保存全量数据哨兵节点负责监控和触发主从切换不参与数据读写所以它不会带来分片效果。集群模式自带高可用能力不需要额外部署哨兵每个主节点都有自己的从节点主从切换靠集群内部的投票机制完成。还有一个很值得聊的设计问题是“集群模式下怎么保证强一致性”。Redis 集群默认的持久化和复制机制决定了它并不能保证强一致性主从之间是异步复制的主节点执行完写请求后就给客户端返回了数据同步到从节点是后台异步进行的。因此主节点宕机时总有一部分最新写入的数据可能还没来得及同步到从节点从节点晋升为主节点后这部分数据就丢了。这是分布式系统在 CAP 中的典型取舍Redis 选择的是高可用 A 和分区容错性 P牺牲了一定的 C。方案设计的角度问问自己几个问题系统要不要用集群模式判断标准是数据量和写入吞吐量是否真的超过了单机能力如果用集群key 的设计是否能避开多键操作限制如果暂时用不到集群未来扩展的可能性有多大早期的 key 设计需要提前考虑 hash tag 方案。8. 集群模式搭建中最容易踩的坑和排查技巧汇总最后把实操中经常遇到的一些问题整理成速查表格问题现象可能原因解决思路cluster_state:fail某个主节点不可用且无可用从节点尽快恢复宕机节点检查内存和磁盘MOVED 错误频繁出现客户端未启用集群模式或使用旧版客户端使用支持集群协议的客户端连接访问CROSSSLOT 错误多 key 操作横跨不同槽位改用 hash tag或拆分请求逐批处理cluster nodes 显示某个节点一直是 handshake 状态新节点加入失败可能是网络或端口问题确保集群总线端口可通重启新节点再添加写入报 CLUSTERDOWN Hash slot not served槽位分配不完整或某个主节点无从副本检查槽位是否 0-16383 全覆盖从节点是否正常扩容后新节点无数据只 add-node 没有 reshard需要执行 reshard 迁移槽位到新节点主节点重启后变从节点故障转移后旧主节点降级是正常行为确认集群仍是高可用状态必要时手动指定角色客户端报 max redirects exceeded集群拓扑更新期间访问异常检查集群状态减少节点抖动对客户端的影响再补充几个部署层面的边界问题。集群模式下不要忘了给每个端口对应的集群总线端口开放防火墙否则新节点一直加不进来。节点状态文件 nodes-7001.conf 一旦丢失节点就找不到自己的集群身份这种场景需要先把节点清空后重新加入集群手动平衡槽位。生产环境做集群上线前要对每个节点设置相同的 maxmemory 和内存淘汰策略不然不同的节点开始淘汰数据的时间点不同会造成部分数据先消失的诡异现象。集群模式下日志与监控在故障处理中的价值非常高。有了监控面板节点宕机时能快速看到是哪个实例的哪个指标先异常再看日志定位具体问题。我的建议是把 INFO 命令输出的指标采集到 Prometheus 这类监控系统里重点指标包括connected_clients、used_memory、instantaneous_ops_per_sec、rejected_connections、cluster_state。回到最初选型的那句话数据量 10G 以内单机没有问题十亿级 key 的业务数据再多就要考虑集群分片和更精细的缓存治理方案。踩过几次集群切换的坑之后我个人的体会是集群模式天然降低了单点故障的风险但提升了设计复杂度。选择集群前先衡量二三十秒的故障影响和几天的改造运维成本通常答案就会比较清晰。如果集群已经上线那就在 key 设计和监控告警上下足功夫这两件事做扎实了比再贵的服务器都顶用。