1. 集群扩缩容的整体设计思路与方案选型Redis Cluster 作为官方原生的分布式方案核心优势在于去中心化、数据自动分片以及内置的高可用机制。但很多团队在业务初期部署集群时往往只按当时的业务量规划了固定数量的主节点等到流量上涨、内存吃紧或者需要做资源回收时才发现扩缩容这件事远比想象中复杂。我自己经历过好几次生产环境的扩容和缩容踩过的坑足够写满一个笔记本所以这里把整套流程和背后的逻辑完整梳理一遍。1.1 为什么扩缩容不能简单粗暴地加机器Redis Cluster 的数据分片基于哈希槽Hash Slot机制整个集群固定有 16384 个槽位。每个主节点负责其中一部分槽位键通过 CRC16 计算后对 16384 取模决定落在哪个槽。这意味着扩容不是把新节点加进去就完事了必须把一部分槽位从旧节点迁移到新节点同时槽位里已有的数据也要跟着搬过去。缩容则反过来要先把待下线节点的槽位全部迁走确认没有数据残留后才能安全移除。这个过程涉及槽位状态标记、键迁移、客户端重定向等一系列操作任何一个环节处理不当都可能导致数据丢失或集群不可用。我见过有同学直接CLUSTER ADDSLOTS手动分配槽位结果新旧节点槽位冲突整个集群进入 fail 状态。所以理解槽位迁移的原子性和状态机是第一步。1.2 扩缩容方案对比手动迁移 vs 工具辅助目前主流的操作方式有三种方案操作方式优点缺点适用场景redis-cli --cluster官方命令行工具无需额外依赖命令直观大批量迁移时耗时较长中途失败恢复麻烦中小规模集群槽位迁移量不大第三方管理平台可视化界面操作操作简单有进度展示依赖平台稳定性底层仍是调用官方命令运维自动化程度较高的团队自研脚本基于 CLUSTER 命令封装可定制化支持断点续传开发维护成本高容易引入 bug超大规模集群有特殊迁移策略需求对于绝大多数团队来说redis-cli --cluster自带的reshard和rebalance功能已经够用了。我个人的建议是集群规模在 50 个主节点以内直接用官方工具超过这个量级再考虑自研或平台化方案。因为官方工具在迁移过程中会输出详细的进度信息出问题时也容易定位。1.3 扩容与缩容的核心差异扩容和缩容虽然都是槽位迁移但风险点和操作顺序完全不同。扩容是从无到有新节点初始状态是空的迁移过程中源节点和目标节点同时持有部分槽位的数据客户端访问时可能被重定向。这个阶段集群的负载会短暂升高因为迁移本身要消耗 CPU 和网络带宽。缩容是从有到无待下线节点上的数据必须先全部迁走然后才能执行CLUSTER FORGET将其移除。如果槽位没迁干净就强制下线那部分槽位会处于无人负责的状态集群直接不可用。所以我的操作习惯是扩容选在业务低峰期做缩容更要选低峰期而且缩容前一定要反复确认槽位归属。2. 扩容前的环境准备与关键参数检查动手之前先把基础工作做扎实。我见过太多人上来就敲命令结果因为集群状态不健康导致迁移中断最后不得不回滚。2.1 集群健康状态自检清单执行任何扩缩容操作前必须确认以下指标全部正常集群状态cluster_state:ok不能是fail槽位覆盖16384 个槽位全部有节点负责没有遗漏节点握手所有节点互相认识cluster_known_nodes数量等于实际节点数主从关系每个主节点都有对应的从节点且复制偏移量正常内存水位各节点内存使用率低于maxmemory的 70%预留足够空间给迁移过程用一条命令快速检查redis-cli --cluster check 10.0.0.1:7000这条命令会输出集群的完整拓扑、槽位分布、节点角色和健康状态。如果看到[OK]标记全部通过才能继续下一步。注意如果集群开启了密码认证需要在命令后加-a参数或者配置REDISCLI_AUTH环境变量。生产环境不建议在命令行明文传密码容易被history记录。2.2 新节点的配置对齐新加入的节点配置必须和现有节点保持一致否则会出现各种诡异问题。重点检查这几项# 集群模式必须开启 cluster-enabled yes # 节点超时时间建议和现有节点一致 cluster-node-timeout 15000 # 开启槽位迁移时的自动重定向 cluster-require-full-coverage no # 内存淘汰策略根据业务选择 maxmemory-policy allkeys-lru # 持久化配置建议和现有节点相同 appendonly yes appendfsync everysec特别强调cluster-require-full-coverage这个参数。如果设为yes只要有一个槽位不可用整个集群就拒绝服务。扩缩容过程中槽位状态会短暂变化设为no可以避免误判。当然这个参数只是临时保护迁移完成后可以根据业务容忍度决定是否改回。2.3 客户端兼容性确认槽位迁移期间客户端会收到MOVED和ASK两种重定向响应。主流客户端库都支持自动处理但有些老版本或配置不当的客户端可能会报错。Jedis需要开启testOnBorrow和testWhileIdle确保连接有效性Lettuce默认支持集群拓扑刷新但建议设置cluster-topology-refresh-period为 30 秒redis-py使用RedisCluster类不要用单机Redis类连接集群我踩过的一个坑是某业务用的是老版本 Jedis迁移过程中频繁报JedisMovedDataException后来升级客户端并调整连接池参数才解决。所以扩缩容前一定要和业务方确认客户端版本和配置。3. 扩容实操从加入节点到槽位均衡假设现有集群是 3 主 3 从部署在 6 台机器上现在要扩容到 5 主 5 从。新增的 2 主 2 从分别部署在另外 4 台机器上。3.1 新节点启动与集群加入先在新机器上启动 Redis 实例配置文件参考上一节的模板。启动后确认进程正常redis-server /etc/redis/redis-7000.conf redis-cli -p 7000 ping返回PONG说明单机启动成功。接下来把新节点加入集群。有两种方式方式一使用cluster meet手动加入# 在任意现有节点上执行让集群认识新节点 redis-cli -p 7000 cluster meet 10.0.1.1 7000 redis-cli -p 7000 cluster meet 10.0.1.2 7000方式二使用redis-cli --cluster add-noderedis-cli --cluster add-node 10.0.1.1:7000 10.0.0.1:7000 redis-cli --cluster add-node 10.0.1.2:7000 10.0.0.1:7000两种方式效果一样但add-node会输出更友好的提示信息。加入后执行cluster nodes应该能看到新节点状态是connected但槽位数为 0。3.2 为新主节点分配从节点新加入的主节点还没有从节点需要把新加入的从节点挂上去。假设新从节点是 10.0.1.3:7000 和 10.0.1.4:7000# 在从节点上执行指定要复制的主节点 ID redis-cli -p 7000 -h 10.0.1.3 cluster replicate 新主节点ID redis-cli -p 7000 -h 10.0.1.4 cluster replicate 另一个新主节点ID主节点 ID 可以通过cluster nodes获取是一串 40 位的十六进制字符串。复制关系建立后从节点状态会变成slave并且开始同步数据。提示新主节点此时还没有槽位从节点同步的是空数据集速度很快。等槽位迁移完成后从节点会自动同步新增的数据。3.3 槽位迁移reshard 命令详解这是扩容最核心的步骤。redis-cli --cluster reshard命令支持交互式和命令行两种模式。交互式适合第一次操作命令行适合脚本化。交互式操作流程redis-cli --cluster reshard 10.0.0.1:7000执行后会依次询问要迁移多少个槽位假设从 3 主扩到 5 主理想情况是每个主节点负责 16384/5 ≈ 3277 个槽。现有 3 个主节点各负责约 5461 个槽需要各迁出约 2184 个槽给新节点。所以总共迁移 2184 × 2 4368 个槽平均分给 2 个新节点每个新节点接收 2184 个槽。接收节点是哪个输入新主节点的 ID。源节点是哪些可以输入all表示从所有现有主节点均匀抽取也可以指定具体节点 ID。确认迁移计划。工具会列出迁移方案输入yes开始执行。命令行模式redis-cli --cluster reshard 10.0.0.1:7000 \ --cluster-from 源节点ID1,源节点ID2,源节点ID3 \ --cluster-to 目标节点ID \ --cluster-slots 2184 \ --cluster-yes这种方式适合批量操作比如给两个新节点分别迁移槽位可以写个循环脚本。3.4 迁移过程中的监控要点槽位迁移不是瞬间完成的特别是数据量大时可能持续几十分钟甚至几小时。期间需要重点关注迁移进度reshard命令会实时输出Moving slot xxx from xxx to xxx观察进度是否在推进集群状态另开终端执行cluster info确认cluster_state始终为ok内存变化源节点内存可能短暂升高迁移缓冲区目标节点内存逐步上升客户端影响观察业务监控看是否有大量MOVED重定向或超时报警我一般会同时开三个终端一个跑迁移命令一个循环执行cluster info一个 tail 业务日志。这样任何异常都能第一时间发现。3.5 迁移完成后的验证所有槽位迁移完成后执行一次全面检查redis-cli --cluster check 10.0.0.1:7000确认输出中所有 16384 个槽位都有归属新节点槽位数符合预期每个主节点都有对应的从节点没有[ERR]或[WARN]标记然后做一次数据抽样验证随机选几个 key 看是否能正常读写redis-cli -c -p 7000 set test_key_001 hello redis-cli -c -p 7000 get test_key_001-c参数表示启用集群模式客户端会自动跟随重定向。4. 缩容实操安全下线节点的完整流程缩容比扩容更需要谨慎因为一旦槽位迁不干净就强制下线集群直接崩。我的原则是宁可多花时间确认也不要抢那几分钟。4.1 确定待下线节点与槽位规划假设现在要把 5 主缩回 3 主需要下线 2 个主节点。首先查看当前槽位分布redis-cli --cluster check 10.0.0.1:7000假设待下线节点 A 负责 3277 个槽节点 B 负责 3277 个槽。这 6554 个槽要重新分配给剩余的 3 个主节点每个主节点多承担约 2185 个槽。规划好之后先迁移槽位再移除节点。顺序不能反。4.2 槽位迁移把数据全部搬走使用reshard命令把待下线节点的槽位迁到其他节点redis-cli --cluster reshard 10.0.0.1:7000 \ --cluster-from 待下线节点A的ID \ --cluster-to 接收节点ID \ --cluster-slots 3277 \ --cluster-yes如果有多个接收节点可以分批迁移比如先迁 1600 个槽到节点 C再迁 1677 个槽到节点 D。这样负载更均衡。迁移过程中同样要监控集群状态和迁移进度。特别注意的是缩容迁移的源节点是待下线节点迁移完成后该节点的槽位数应该为 0。4.3 确认槽位清空与数据一致性迁移完成后执行cluster nodes确认待下线节点的slots字段为空。然后做一次数据一致性抽查# 在待下线节点上执行看是否还有键 redis-cli -p 7000 dbsize # 如果返回 0 或接近 0说明数据基本迁完 # 如果还有大量键说明迁移不完整需要排查这里有个细节dbsize返回 0 并不绝对代表没有数据因为可能存在过期键或迁移残留。更稳妥的方式是用SCAN抽样检查redis-cli -p 7000 --scan --count 100 | head -20如果抽样结果为空基本可以确认数据已清空。4.4 移除节点FORGET 与 DEL-NODE 的选择确认槽位清空后就可以移除节点了。有两种方式方式一CLUSTER FORGET# 在集群中其他所有节点上执行忘记待下线节点 redis-cli -p 7000 cluster forget 待下线节点ID这个命令只是让其他节点不再认识该节点但该节点本身还在运行需要手动停掉进程。方式二redis-cli --cluster del-noderedis-cli --cluster del-node 10.0.0.1:7000 待下线节点ID这个命令会自动在其他节点上执行FORGET并关闭待下线节点的进程。更方便推荐使用。注意如果待下线节点还有从节点需要先处理从节点。要么把从节点重新挂到其他主节点上要么一起下线。直接删除主节点而从节点还在会导致从节点不断尝试重连一个不存在的 master产生大量日志。4.5 缩容后的集群均衡检查所有待下线节点移除后执行一次rebalance让槽位分布更均匀redis-cli --cluster rebalance 10.0.0.1:7000 --cluster-use-empty-masters--cluster-use-empty-masters参数表示允许空主节点参与槽位分配。如果缩容后所有节点都有槽位这个参数可以不加。rebalance 完成后再次执行check确认槽位分布均匀、集群状态正常。5. 常见问题与排查技巧实录扩缩容过程中遇到的问题五花八门这里整理几个我实际踩过的坑和解决方法。5.1 迁移卡住不动怎么办现象reshard命令输出停在某个槽位长时间没有进展。排查思路检查源节点和目标节点的网络连通性ping和telnet端口都要测查看源节点日志是否有CLUSTER SETSLOT相关错误检查是否有大 key 导致迁移超时用redis-cli --bigkeys扫描确认cluster-node-timeout设置是否过短迁移期间节点心跳可能延迟解决方法如果是大 key 问题可以先把大 key 拆分或删除再迁移。如果是网络问题修复网络后重新执行reshard工具会从断点继续。5.2 迁移后客户端报 MOVED 错误现象槽位迁移完成后部分客户端仍然报MOVED错误。原因客户端缓存的集群拓扑没有更新。解决方法重启客户端应用强制刷新拓扑检查客户端配置确保开启了拓扑自动刷新对于 Jedis可以调用jedisCluster.getClusterNodes()手动触发刷新5.3 缩容后集群状态变为 fail现象移除节点后cluster info显示cluster_state:fail。原因大概率是槽位没有完全迁移有部分槽位处于无人负责状态。解决方法执行cluster check查看哪些槽位缺失如果缺失的槽位在已下线节点上需要把该节点重新加入集群迁移完槽位后再下线如果槽位数据已丢失只能手动CLUSTER ADDSLOTS把槽位分配给现有节点但数据无法恢复这个问题的根源还是操作前没有仔细确认槽位清空。所以再次强调缩容前必须反复确认待下线节点槽位数为 0。5.4 迁移速度慢的优化技巧如果集群数据量大迁移可能持续数小时。几个加速技巧调整cluster-migration-barrier降低这个值可以让从节点更积极地参与迁移但会增加主从切换风险建议保持默认分批迁移不要一次性迁移所有槽位分多次小批量迁移每次迁移后观察集群负载避开业务高峰迁移消耗 CPU 和带宽低峰期操作对业务影响最小使用 pipelinereshard命令内部已经使用了批量操作但可以通过调整--cluster-pipeline参数控制每次迁移的 key 数量默认是 10可以适当调大5.5 常见问题速查表问题现象可能原因排查命令解决方法迁移卡住大 key、网络抖动redis-cli --bigkeys拆分大 key检查网络MOVED 错误客户端拓扑未更新cluster nodes重启客户端开启自动刷新集群 fail槽位缺失cluster check重新加入节点迁移槽位迁移速度慢数据量大、配置保守cluster info分批迁移调整 pipeline从节点同步失败主从版本不一致info replication统一版本检查网络6. 生产环境操作的经验与避坑建议最后分享几条我个人在实际操作中总结的经验都是文档里不会写的。第一永远不要在周五下午做扩缩容。万一出问题周末没人处理业务受影响的时间会被无限拉长。我一般选周二或周三的凌晨这样即使有问题也有足够的时间在工作日解决。第二操作前一定要备份。虽然 Redis Cluster 的迁移机制本身是安全的但人为误操作的风险永远存在。备份方式可以是BGSAVE生成 RDB 文件也可以用redis-cli --cluster backup导出槽位配置。我习惯两者都做RDB 用于数据恢复槽位配置用于快速重建集群拓扑。第三先在测试环境完整走一遍流程。生产环境和测试环境的差异往往超出预期比如网络延迟、数据量、客户端版本等。在测试环境把整个扩缩容流程跑通记录每一步的耗时和输出生产操作时心里才有底。第四迁移期间不要做其他变更。比如不要同时升级客户端、不要调整业务配置、不要重启其他服务。变量越多出问题时越难定位。第五关注迁移对业务的影响。槽位迁移会导致部分请求被重定向延迟可能短暂升高。如果业务对延迟敏感可以考虑在迁移期间临时调大客户端超时时间或者用灰度方式逐步迁移槽位。第六缩容后及时清理配置。下线节点后记得从监控系统、配置中心、DNS 等地方移除对应条目避免残留配置导致后续误操作。第七记录操作日志。每次扩缩容都记录操作时间、操作人、迁移槽位数、耗时、遇到的问题和解决方法。这些记录在后续排查问题或做容量规划时非常有用。Redis Cluster 的扩缩容本身并不复杂核心就是槽位迁移和节点管理。但生产环境的复杂性在于任何一个小问题都可能被放大。把准备工作做足把监控做到位把回滚方案想清楚剩下的就是按部就班执行。我在实际使用中发现最耗时的往往不是迁移本身而是迁移前的确认和迁移后的验证。这两步做扎实了整个操作就成功了一大半。
阅读完成 · 觉得有帮助?