Redis Cluster 在线扩容与缩容完整流程

举报
yd_227606267 发表于 2026/11/12 07:31:02 2026/11/12
【摘要】 覆盖加节点分槽与减节点迁槽回收的完整流程和验证清单

集群容量告急要加机器、机器要到期回收要下线,这是运维 Redis Cluster 最常见的两类变更。扩缩容的核心动作其实只有一个——迁槽,但它牵涉节点加入、副本归属、客户端拓扑、业务延迟四个层面,任何一步顺序错了都可能把故障转移带出来。本文基于 Redis 7,给出从准备到验证的完整步骤清单。

一、变更前的准备

扩缩容之前先做三件事。第一,确认目标:是新增容量还是下线机器,新节点规格尽量与存量对齐(CPU、内存、磁盘型号),版本必须一致。第二,网络放通两个端口:业务端口 6379 和集群总线端口(默认为业务端口加 10000,Redis 7 也可以用 cluster-port 显式指定)。总线端口不通,节点永远握手不上。第三,认证一致:新节点要配置与集群相同的 masterauth、requirepass 或 ACL,否则加入后无法建立复制与心跳。最后把变更前的 CLUSTER NODES 输出存档,出问题时是唯一的对照基准。

准备的第四件事常被省略:确认集群当前健康。带着 cluster_state:fail 或存在 pfail 节点去做扩缩容,会把一个待恢复的故障叠加成两个。变更前至少确认三条:

redis-cli --cluster check 10.0.0.11:6379
# 输出末尾
# [OK] All 16384 slots covered.
# [OK] All nodes agree about slots configuration.

redis-cli -p 6379 cluster info | grep -E "cluster_state|cluster_slots_fail|cluster_known_nodes"
# cluster_state:ok
# cluster_slots_fail:0
# cluster_known_nodes:6

第一条确认槽全覆盖且节点对槽配置没有分歧,第二条确认没有确认下线的节点。任何一条不满足,先修集群再做容量变更。

二、扩容:加节点、分槽、验证

# 1. 新节点加入集群(此时是没有任何槽的空节点)
redis-cli --cluster add-node 10.0.0.17:6379 10.0.0.11:6379

# 2. 让槽位在主节点间按权重摊平(权重全为 1 即均分)
redis-cli --cluster rebalance 10.0.0.11:6379 \
  --cluster-weight <主1 id>=1,<主2 id>=1,<主3 id>=1,<新主 id>=1

# 3. 也可以单独给新节点迁一部分槽(详见 reshard 一文)
redis-cli --cluster reshard 10.0.0.11:6379 \
  --cluster-from <老主 id> --cluster-to <新主 id> \
  --cluster-slots 2000 --cluster-yes

# 4. 直接扩一个副本节点
redis-cli --cluster add-node 10.0.0.18:6379 10.0.0.11:6379 \
  --cluster-slave --cluster-master-id <要跟随的主节点 id>

# 5. 验证
redis-cli --cluster check 10.0.0.11:6379
redis-cli -c -p 6379 cluster info

rebalance 会按权重把所有主节点的槽重新摊平,适合新节点一次性到位;reshard 更精确,适合只给新主迁一部分槽。要注意:扩容摊平的是槽,不是 key——同一槽内的 key 不会因此分散,如果容量压力来自某个巨型 Hash Tag,扩容解决不了,必须业务侧拆分。

两种分槽方式的差别不止"精确度",还在于对业务的影响范围。rebalance 是全局计算,它会把槽从"超出目标值"的节点搬给"不足"的节点,可能同时触发多个节点之间的迁移,窗口内 ASK 重定向的影响面覆盖整个集群;reshard 只涉及一对源和目标,其余节点完全不受影响。因此生产上更稳的做法是:先 rebalance 时用 --cluster-use-empty-masters 只把空节点作为目标,避免在存量节点之间来回搬;或者干脆按节点逐个 reshard,一次只动一个源。

还有一个参数值得了解:--cluster-simulate。加上它,rebalance 只打印将要执行的计划而不真的迁移,可以先把方案跑一遍,确认迁移量、涉及节点都符合预期再执行。对没有把握的变更,这一步几乎零成本。

三、缩容:先迁槽,再 del-node

# 1. 把待删主节点的槽全部迁给留存节点(5461 个槽可分多批执行)
redis-cli --cluster reshard 10.0.0.11:6379 \
  --cluster-from <待删主 id> --cluster-to <留存主 id> \
  --cluster-slots 5461 --cluster-yes

# 2. 确认该节点已经没有任何槽
redis-cli -c -p 6379 cluster nodes | grep <待删主 id>

# 3. 摘除节点(无槽的副本可以跳过前两步直接摘)
redis-cli --cluster del-node 10.0.0.11:6379 <待删节点 id>

del-node 做两件事:向其余所有节点发送 CLUSTER FORGET 清除该节点的信息,并让目标节点自行下线。如果被删的主节点带有从库,要先决定从库的去向:执行 CLUSTER REPLICATE <其他主 id> 让它跟随新主,或者一并删除——只要集群整体副本充足,残留的孤儿主会按 cluster-migration-barrier 的配置自动从别的主节点「借」一个从库过来。

四、变更窗口与验证清单

整个变更安排在业务低峰,迁移期间盯三样东西:SLOWLOG GET 是否出现 MIGRATE 相关慢条目、客户端是否出现持续的 MOVED/TRYAGAIN 报错、各主节点内存水位。变更结束后跑一遍固定清单:--cluster check 全部 OK、16384 个槽全部有主、每个主节点至少一个健康副本、应用端日志无重定向风暴、关键业务链路回归通过。

缩容有一个容易被忽略的风险:迁走槽不等于释放内存。KEY 搬走之后,源节点的内存分配器并不会立刻把空闲页还给操作系统,used_memory 下降但 used_memory_rss 可能仍然很高。如果接着要做机器回收,这没问题;但如果是想在原机器上再部署其他服务,就需要关注这个滞后。

redis-cli -p 6379 INFO memory | grep -E "used_memory_human|used_memory_rss_human|mem_fragmentation_ratio"
# used_memory_human:3.21G
# used_memory_rss_human:6.84G
# mem_fragmentation_ratio:2.13

迁槽后碎片率翻到 2 以上是典型现象:大量 key 被删除,留下零散的空闲块。此时可以开 activedefrag 或者重启节点回收,但重启意味着又要做一次全量同步,成本未必比重建机器低。实践中的建议是:缩容后如果碎片率高于 1.5,直接在低峰重启该节点,让从库接住流量,不要指望分配器自己收敛。

变更窗口的排期还要考虑一个隐藏依赖:客户端拓扑缓存。扩容后新增节点对老客户端是未知的,访问新槽时必然先撞一次 MOVED。如果客户端全量刷新间隔很长,这段"撞墙期"能持续几分钟。把应用滚动重启安排在变更结束之后,或者提前把客户端的拓扑刷新间隔调短,可以把这段时间压到最短。

五、变更后的容量复核

扩缩容完成后不代表事情结束,还需要用一周的观察数据回答一个问题:这次变更是否真的达到了目的。三类指标需要复核:

  • 单节点水位:每个主节点的 used_memory 与 QPS 是否回到目标区间。若某个节点仍然明显偏高,说明倾斜还在,扩容只是把绝对水位降了一点。
  • 重定向次数:客户端侧的 MOVED 与 ASK 计数应在变更后几十分钟内归零。持续有 MOVED 说明有客户端拓扑未刷新,或集群内部对槽归属存在分歧。
  • 慢命令分布:SLOWLOG GET 100 里 MIGRATE 相关条目应在变更结束后消失。若仍有残留,通常是某个槽的迁移没收尾。

复核的意义在于:扩缩容是低频操作,每次都是一次校准机会。把"变更前水位、变更后水位、重定向峰值、慢命令数量"四个数字记进变更记录,下次做同样的操作就有基线可比,也能发现"扩容后水位没降"这类更深层的问题。

常见坑 / 最佳实践

  1. 绝不直接 kill 进程来缩容。 主节点无预警下线会触发故障转移,如果它的从库也已删除,槽将失主,cluster_state 变为 fail,全集群拒绝服务。顺序永远是:先迁槽、确认无槽、再 del-node。
  2. 规格不一致时不要默认 rebalance。 槽位均摊后,小内存节点会先 OOM。此时按内存容量设置 weight(例如小节点 0.5),而不是全部 1。
  3. 扩容后关注客户端拓扑刷新。 Lettuce 开启自适应刷新(enableAllAdaptiveRefreshTriggers)即可;老版本 Jedis 依赖 MOVED 触发刷新,大量请求会先撞一次重定向,滚动发布窗口要提前安排。
  4. 删主节点前先安置它的从库。 先让从库改投其他主或随迁,避免主库在裸奔期间又触发全量同步,叠加出第二起故障。
  5. 变更与其他运维任务错峰。 reshard 期间的 MIGRATE 阻塞叠加备份任务、bigkeys 全库扫描,任何一项都可能把延迟推过业务阈值,排期上必须互相隔离。

小结

  • 扩缩容前确认规格、双端口连通、认证一致,并留存 CLUSTER NODES 快照作为回退基准。
  • 扩容用 add-node 加入空节点,再用 rebalance 摊平或 reshard 精确分配,扩副本用 --cluster-slave 指定归属。
  • 缩容铁律是先迁槽后摘节点,del-node 会自动向集群广播 FORGET 并让目标节点下线。
  • 扩容只摊平槽不摊平 key,巨型 Hash Tag 造成的容量压力要靠业务拆分解决。
  • 变更以低峰窗口加固定验证清单收尾,check、槽覆盖、副本数、客户端错误四项缺一不可。
【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。