1. 当节点删除变成"拆盲盒"

上周三凌晨两点,我盯着监控屏幕上持续攀升的内存曲线,决定对线上Redis集群实施缩容。这本该是常规操作,却在执行redis-cli --cluster del-node时突然报错:"Node is not empty, cannot remove it"。就像拆盲盒拆到了雷款手办,我的运维生涯突然充满了"惊喜"。

让我们通过具体场景还原这个"案发现场"。假设我们有个3主3从的Redis集群:

# 当前集群节点分布(Redis 5.0+环境)
节点类型   节点ID              槽位范围       主机地址
Master     d0994b8b...     0-5460        172.16.1.101:6379
Master     5ef3c7a1...     5461-10922    172.16.1.102:6379
Master     8b327fc6...     10923-16383   172.16.1.103:6379
Slave      7a1d5f...       -             ...(省略从节点信息)

2. 错误操作示范剧场

2.1 第一幕:直男式删节点

菜鸟小张试图删除172.16.1.103节点:

# 直接执行删除命令(错误示范)
redis-cli --cluster del-node 172.16.1.103:6379 8b327fc6...

系统立即报错:

[ERR] Node 172.16.1.103:6379 is not empty! 
Reshard data away and try again.

2.2 第二幕:暴力迁移闯大祸

老司机老王决定手动迁移槽位:

# 暴力迁移所有槽位(危险操作)
redis-cli --cluster reshard 172.16.1.103:6379 \
    --cluster-from 8b327fc6... \
    --cluster-to d0994b8b... \
    --cluster-slots 16384 \
    --cluster-yes

这种操作会导致:

  1. 超过单节点槽位上限(16384总槽位)
  2. 迁移过程中客户端访问异常
  3. 潜在的数据不一致风险

3. 正确操作指南针

3.1 槽位迁移标准流程

# 查看当前槽位分布
redis-cli --cluster check 172.16.1.101:6379

# 计算需迁移槽位数(假设要迁移10923-16383共5461个槽位)
MIGRATION_COUNT=5461

# 启动安全迁移(推荐使用自动化迁移)
redis-cli --cluster reshard 172.16.1.101:6379 \
    --cluster-from 8b327fc6... \
    --cluster-to d0994b8b... \
    --cluster-slots $MIGRATION_COUNT \
    --cluster-timeout 30000 \
    --cluster-pipeline 10

# 验证迁移结果(必须步骤!)
redis-cli --cluster check 172.16.1.101:6379 | grep "covered slots"

3.2 删除节点前必检清单

# 检查1:槽位是否清空
redis-cli -h 172.16.1.103 cluster slots | wc -l

# 检查2:是否有从节点残留
redis-cli -h 172.16.1.103 cluster nodes | grep "slave"

# 检查3:集群健康状态
redis-cli --cluster check 172.16.1.101:6379

4. 技术深潜区

4.1 Gossip协议下的节点失效

当删除主节点时,集群通过Gossip协议传播节点失效信息。这个过程可能需要:

# 手动触发状态更新(当自动传播异常时)
redis-cli -h 172.16.1.101 cluster forget 8b327fc6...

4.2 槽位迁移的原子性保障

Redis使用CLUSTER SETSLOT命令的三阶段操作:

  1. IMPORTING状态:目标节点准备接收槽位
  2. MIGRATING状态:源节点准备迁移数据
  3. NODE状态:完成所有权转移
# 查看槽位状态(技术人必备)
redis-cli -h 172.16.1.103 cluster slots

5. 避坑百科全书

5.1 从节点处理陷阱

删除主节点前必须确保:

# 查看并处理从节点
redis-cli --cluster info 172.16.1.101:6379 | grep "replica"

5.2 客户端缓存雪崩

删除节点后未更新客户端配置可能导致:

MOVED 12539 172.16.1.103:6379  # 已删除的节点地址

5.3 集群状态异常处理

当出现CLUSTERSTATE:fail时:

# 强制修复集群(慎用!)
redis-cli --cluster fix --cluster-search-multiple-owners

6. 技术选型辩证法

6.1 适用场景

  • 云环境资源弹性伸缩
  • 硬件更换维护窗口期
  • 版本升级前的节点隔离

6.2 优劣分析

优势

  • 支持在线缩容(对比Codis需要停机)
  • 细粒度槽位控制(对比Twemproxy静态分片)

劣势

  • 操作复杂度指数级上升
  • 网络分区风险增加

7. 血泪经验总结

  1. 槽位迁移必须分批执行:建议每次迁移不超过500个槽位
  2. 监控必须覆盖迁移过程:重点关注moved_redirects指标
  3. 客户端必须支持重定向:推荐使用Lettuce客户端
  4. 删除后必须清理配置:特别是哨兵系统中的监控配置
# 推荐操作后执行(配置清理示例)
sentinel remove redis-cluster-node-103

8. 未来演进方向

新一代Proxy方案(如Redis Cluster Proxy)正在尝试:

  • 解耦客户端与集群拓扑
  • 提供平滑的节点变更体验
  • 自动化的槽位迁移策略

但截至目前,掌握原生集群管理技能仍然是Redis运维人员的必修课。就像老司机不能完全依赖自动驾驶,关键时刻还是得靠手动挡操作来力挽狂澜。

记住:在分布式系统的世界里,每一次优雅的缩容操作,都是运维工程师与计算机达成的一次完美共谋。