Redis100篇 - Redis集群缩容怕丢数据 节点下线+数据迁移全流程
Redis集群安全缩容指南:零数据丢失的节点下线流程 摘要:本文详细讲解Redis集群缩容的安全操作流程,核心围绕"先迁移后下线"原则。首先分析直接下线节点的三大风险:集群状态异常、数据永久丢失和服务中断。随后提供完整操作步骤:1)评估集群状态与节点角色;2)数据备份;3)使用redis-cli工具或手动迁移槽位数据;4)确认迁移完成。通过mermaid流程图展示数据迁移过程,

👋 大家好,欢迎来到我的技术博客!
💻 作为一名热爱 Java 与软件开发的程序员,我始终相信:清晰的逻辑 + 持续的积累 = 稳健的成长。
📚 在这里,我会分享学习笔记、实战经验与技术思考,力求用简单的方式讲清楚复杂的问题。
🎯 本文将围绕Redis这个话题展开,希望能为你带来一些启发或实用的参考。
🌱 无论你是刚入门的新手,还是正在进阶的开发者,希望你都能有所收获!
文章目录
Redis100篇 - Redis集群缩容怕丢数据 节点下线+数据迁移全流程 🛑💾
在构建高可用、高性能的分布式系统时,我们常常谈论“扩容”——如何增加节点来应对流量洪峰和数据增长。但鲜有人深入探讨一个同样重要却更令人紧张的操作:缩容(Scale-in)。
当业务进入平稳期,或资源利用率长期偏低时,为了节省成本、优化资源分配,我们不得不面对一个问题:如何安全地从 Redis Cluster 中移除一个或多个节点?
这可不是简单的“关机”或“删除容器”这么简单。如果操作不当,轻则导致部分数据不可访问,重则造成数据永久丢失,引发线上事故。因此,许多运维和开发人员对 Redis 集群缩容心存畏惧,“怕丢数据”成了普遍的心理障碍。
本文将彻底揭开 Redis 集群缩容的神秘面纱,提供一份 完整、安全、可落地的数据迁移与节点下线全流程指南。我们将结合真实命令、Java 代码示例、mermaid 流程图和权威外链,带你一步步完成一次“零数据丢失”的优雅缩容。让你从此不再“怕丢数据”,从容应对资源调整。🛡️
⚠️ 为什么缩容如此“危险”?常见误区与风险
在开始操作之前,我们必须认清缩容的风险来源,避免落入常见误区。
❌ 误区一:直接关闭节点 = 安全下线
最危险的操作莫过于直接 kill 一个 Redis 进程或停止 Docker 容器。这会导致:
- 如果是主节点,其负责的哈希槽将无人管理,
cluster_state变为fail,整个集群不可用。 - 如果是该主节点的从节点也同时宕机或网络隔离,数据将永久丢失。
- 即使有从节点能晋升为主,但这个过程需要时间,期间服务中断。
❌ 误区二:认为“从节点有数据,主节点删了也无所谓”
虽然从节点会复制主节点的数据,但只有主节点才能对外提供写服务。一旦主节点被移除,即使从节点有数据,客户端也无法写入,集群仍然处于“部分不可用”状态。
❌ 误区三:迁移数据时未处理客户端连接
在迁移过程中,如果客户端没有正确处理 MOVED 或 ASK 重定向,可能会持续向旧节点发送请求,导致数据不一致或请求失败。
✅ 缩容核心原则:先迁移,再下线
安全缩容的唯一正确路径是:
1. 将待下线节点负责的哈希槽(及其数据)迁移到其他存活节点。
2. 确认迁移完成且数据一致后,再将该节点从集群中移除。
这个过程与扩容类似,但方向相反。Redis Cluster 提供了完善的命令支持,只要按流程操作,完全可以做到不丢数据、服务不中断。
🧭 缩容前的准备:评估与规划
“磨刀不误砍柴工”,缩容前的准备工作至关重要。
🔍 1. 评估当前集群状态
连接到集群,确认整体健康。
# 连接到任意节点
redis-cli -c -h 192.168.1.101 -p 7000
# 查看集群信息
> CLUSTER INFO
cluster_state:ok
cluster_slots_assigned:16384
cluster_known_nodes:6
cluster_size:3
# ...
确保 cluster_state:ok,所有槽已分配。
📊 2. 分析节点负载与角色
使用 CLUSTER NODES 查看各节点的槽位分布和角色。
> CLUSTER NODES
c7f1f2... 192.168.1.101:7000@17000 master - 0 1678901234567 1 connected 0-5460
a3b2c1... 192.168.1.101:7001@17001 slave c7f1f2... 0 1678901234567 1 connected
d8e9f0... 192.168.1.102:7002@17002 master - 0 1678901234567 2 connected 5461-10922
f1g2h3... 192.168.1.102:7003@17003 slave d8e9f0... 0 1678901234567 2 connected
e4f5g6... 192.168.1.103:7004@17004 master - 0 1678901234567 3 connected 10923-16383
h7i8j9... 192.168.1.103:7005@17005 slave e4f5g6... 0 1678901234567 3 connected
假设我们计划下线 192.168.1.101:7000(Node ID: c7f1f2...)及其从节点 192.168.1.101:7001。
💾 3. 数据备份(强烈建议)
尽管缩容是在线操作,但为防万一,执行一次 BGSAVE。
# 在待下线的主节点上
redis-cli -p 7000 BGSAVE
等待 SAVE 完成,确保有最新的 RDB 快照。
🔄 步骤一:迁移待下线主节点的数据
这是缩容的核心步骤,必须确保所有数据安全迁移到其他主节点。
方法一:使用 redis-cli --cluster reshard 自动迁移(推荐)
Redis 提供了自动化工具,简化迁移流程。
redis-cli --cluster reshard 192.168.1.101:7000
交互式向导流程:
-
How many slots do you want to move?
输入待下线节点负责的槽数。7000节点负责0-5460,共 5461 个槽。输入5461。 -
What is the receiving node ID?
你想把数据迁移到哪个节点?可以输入一个目标节点的node ID,也可以输入all让工具自动平均分配到所有其他主节点。为了负载均衡,推荐输入
all。 -
Source node #1:
输入待下线节点的node ID,即c7f1f2...。
输入done结束。 -
Type ‘yes’ to accept the plan:
工具会生成一个迁移计划,确认无误后输入yes。
命令执行后,Redis 会开始将 5461 个槽位的数据从 7000 节点迁移到其他主节点(7002 和 7004)。
如上图所示,待下线节点 A 的槽位被迁移到 B 和 C,A 变为空壳。
方法二:手动迁移(高级控制)
如果你需要精细控制迁移过程(如分批迁移),可以手动执行。
1. 选择目标节点并设置 IMPORTING
假设将槽位 0-2730 迁移到 7002 节点(d8e9f0...)。
在 7002 节点上执行:
redis-cli -p 7002 CLUSTER SETSLOT 0 IMPORTING c7f1f2...
redis-cli -p 7002 CLUSTER SETSLOT 1 IMPORTING c7f1f2...
# ... 重复到 2730
redis-cli -p 7002 CLUSTER SETSLOT 2730 IMPORTING c7f1f2...
2. 在源节点设置 MIGRATING
在 7000 节点上执行:
redis-cli -p 7000 CLUSTER SETSLOT 0 MIGRATING d8e9f0...
redis-cli -p 7000 CLUSTER SETSLOT 1 MIGRATING d8e9f0...
# ... 重复到 2730
redis-cli -p 7000 CLUSTER SETSLOT 2730 MIGRATING d8e9f0...
3. 迁移 key
使用脚本迁移 key。
# 获取槽位 0 中的 key
KEYS=$(redis-cli -p 7000 CLUSTER GETKEYSINSLOT 0 10)
for key in $KEYS; do
# 迁移 key 到 7002
redis-cli -p 7000 MIGRATE 192.168.1.102 7002 "$key" 0 5000
done
4. 完成槽位迁移
当所有 key 迁移完成后,更新槽位归属。
# 在任意节点执行
redis-cli -p 7000 CLUSTER SETSLOT 0 NODE d8e9f0...
redis-cli -p 7000 CLUSTER SETSLOT 1 NODE d8e9f0...
# ... 重复到 2730
redis-cli -p 7000 CLUSTER SETSLOT 2730 NODE d8e9f0...
5. 清理状态
# 在源节点
redis-cli -p 7000 CLUSTER SETSLOT 0 STABLE
# 在目标节点
redis-cli -p 7002 CLUSTER SETSLOT 0 STABLE
# 对每个槽位重复
重复以上步骤,将剩余槽位(2731-5460)迁移到 7004 节点。
🚫 步骤二:从集群中移除待下线节点
当数据迁移完成后,待下线节点已不再负责任何槽位,可以安全移除。
1. 移除从节点
首先移除从节点 7001。
# 从任意集群节点执行
redis-cli --cluster del-node 192.168.1.101:7000 h7i8j9...
# 注意:第一个参数是集群中任意活跃节点,第二个是待移除节点的 node ID
或使用 CLUSTER FORGET:
# 在任意其他节点上执行
redis-cli -p 7002 CLUSTER FORGET h7i8j9...
redis-cli -p 7004 CLUSTER FORGET h7i8j9...
CLUSTER FORGET <node-id> 会从当前节点的节点列表中移除指定节点。需要在集群中所有其他节点上执行,才能完全移除。
2. 移除主节点
待下线主节点 7000 在数据迁移后已无槽位,可以直接移除。
redis-cli --cluster del-node 192.168.1.102:7002 c7f1f2...
或使用 CLUSTER FORGET 在其他节点上执行。
3. 停止 Redis 进程
确认节点已从 CLUSTER NODES 列表中消失后,可以安全停止 Redis 进程。
# 在 192.168.1.101 服务器上
redis-cli -p 7000 SHUTDOWN
redis-cli -p 7001 SHUTDOWN
或者停止 Docker 容器。
🧪 缩容后的验证
操作完成后,必须进行全面验证。
1. 检查集群状态
redis-cli -c -h 192.168.1.102 -p 7002 CLUSTER INFO
# cluster_state:ok
# cluster_slots_assigned:16384
# cluster_size:2 (now 2 masters)
# cluster_known_nodes:4 (2 masters + 2 replicas)
2. 检查节点拓扑
redis-cli -c -p 7002 CLUSTER NODES
# 确认 c7f1f2... 和 h7i8j9... 已消失
# 确认剩余节点的槽位分配正确
3. 测试数据读写
redis-cli -c -p 7002
> GET user:1001
"1"
> SET product:2001 "Phone"
OK
尝试访问之前由 7000 节点负责的 key,确保能正确重定向并获取数据。
💻 Java 客户端如何应对缩容?
好消息是,使用 Lettuce 或 Jedis 等智能客户端的应用,在缩容过程中几乎无需任何改动。
Lettuce 自动处理缩容
import io.lettuce.core.*;
import io.lettuce.core.cluster.RedisClusterClient;
import io.lettuce.core.cluster.api.StatefulRedisClusterConnection;
import io.lettuce.core.cluster.api.sync.RedisAdvancedClusterCommands;
import java.util.Arrays;
public class RedisClusterShrinkClient {
public static void main(String[] args) {
// 提供集群中任意节点地址(无需包含已下线节点)
RedisURI node1 = RedisURI.create("redis://192.168.1.102:7002");
RedisURI node2 = RedisURI.create("redis://192.168.1.103:7004");
RedisClusterClient clusterClient = RedisClusterClient.create(Arrays.asList(node1, node2));
try {
StatefulRedisClusterConnection<String, String> connection = clusterClient.connect();
RedisAdvancedClusterCommands<String, String> sync = connection.sync();
// 客户端会自动发现集群拓扑变化
sync.set("user:1001", "Alice");
String user = sync.get("user:1001");
System.out.println("User: " + user);
// 即使数据已迁移到新节点,客户端也能正确路由
sync.set("order:2001", "Shipped");
System.out.println("Order: " + sync.get("order:2001"));
// 批量操作
sync.mset("a", "1", "b", "2");
System.out.println("MGET: " + sync.mget("a", "b"));
connection.close();
} catch (Exception e) {
e.printStackTrace();
} finally {
clusterClient.shutdown();
}
}
}
工作原理:
- Lettuce 客户端维护一个本地的槽位映射表。
- 当访问一个
key时,如果收到MOVED响应(指向新节点),客户端会更新本地映射表。 - 下次请求相同槽位的
key时,客户端直接连接新节点,避免重定向。 - 如果客户端尝试连接已下线的节点,会触发拓扑刷新,重新从集群获取最新节点列表。
Jedis 同样支持
import redis.clients.jedis.HostAndPort;
import redis.clients.jedis.JedisCluster;
import java.util.HashSet;
import java.util.Set;
public class JedisShrinkClient {
public static void main(String[] args) {
Set<HostAndPort> nodes = new HashSet<>();
nodes.add(new HostAndPort("192.168.1.102", 7002));
nodes.add(new HostAndPort("192.168.1.103", 7004));
try (JedisCluster cluster = new JedisCluster(nodes)) {
cluster.set("key1", "value1");
System.out.println("key1: " + cluster.get("key1"));
// Jedis 会自动处理 MOVED 重定向和节点变更
} catch (Exception e) {
e.printStackTrace();
}
}
}
📚 延伸阅读
- Redis Cluster Specification - 官方集群规范,深入理解槽位、重定向等机制。
- Scaling Redis Cluster - Redis 官方博客,介绍集群扩缩容实践。
- Lettuce Documentation - 最先进的 Java Redis 客户端,支持异步、响应式编程。
- Redis Administration - 官方运维手册,涵盖备份、监控、故障排查。
🎉 总结
Redis 集群缩容并不可怕,关键在于遵循正确的流程:
- 先迁移数据:使用
--cluster reshard将待下线节点的槽位和数据迁移到其他主节点。 - 再移除节点:使用
--cluster del-node或CLUSTER FORGET将节点从集群中移除。 - 最后停止服务:确认无误后,关闭 Redis 进程。
只要操作规范,配合智能客户端,缩容完全可以做到零数据丢失、服务不中断。希望本文能帮你克服“怕丢数据”的心理障碍,从容应对资源调整,让 Redis 集群的运维更加灵活高效。🎉
🙌 感谢你读到这里!
🔍 技术之路没有捷径,但每一次阅读、思考和实践,都在悄悄拉近你与目标的距离。
💡 如果本文对你有帮助,不妨 👍 点赞、📌 收藏、📤 分享 给更多需要的朋友!
💬 欢迎在评论区留下你的想法、疑问或建议,我会一一回复,我们一起交流、共同成长 🌿
🔔 关注我,不错过下一篇干货!我们下期再见!✨
更多推荐



所有评论(0)