Redis Cluster 槽迁移:MOVED 重定向与拓扑刷新实战

扩容 Redis Cluster 时,业务错误率会突然冒出一串 MOVED。很多人把这当成故障,直接加重试。结果是:迁移窗口里每个请求多一次往返,P99 被拉高,个别客户端还把错误节点缓存死,迁移结束后仍然打偏。

槽迁移不是故障。MOVED 的意思是:这个 key 的哈希槽已经换主人了,请去新节点。客户端必须刷新拓扑,而不是把同一条命令再打回旧节点。

槽、节点、MOVED 分别管什么

计算必须在客户端完成

Cluster 把 16384 个槽摊到节点上。key 用 CRC16 取模得到槽号,再查本地槽表。表过期时,请求打到错误节点,节点返回:

复制代码
MOVED 3999 10.0.1.12:6379

错误写法是把 MOVED 当普通失败,走指数退避:

python 复制代码
import time
import redis

r = redis.Redis(host="10.0.1.11", port=6379)

def incr_wrong(key: str) -> int:
    for i in range(5):
        try:
            return r.incr(key)
        except redis.ResponseError as e:
            if "MOVED" in str(e):
                time.sleep(0.05 * (2 ** i))
                continue
            raise
    raise RuntimeError("incr failed")

退避只会延长错误窗口。正确写法是用 Cluster 客户端,让它消化 MOVED 并更新槽表:

python 复制代码
from redis.cluster import RedisCluster

rc = RedisCluster(
    startup_nodes=[{"host": "10.0.1.11", "port": 6379}],
    decode_responses=True,
    skip_full_coverage_check=True,
)

def incr_ok(key: str) -> int:
    return rc.incr(key)

RedisCluster 收到 MOVED 后会改本地槽映射,下一次同槽请求直打新主人。ASK 不同:它表示迁移进行中,这次去目标节点,但槽表还不能永久改写。客户端必须区分这两种响应。

迁移窗口怎么缩短

先导入再迁移,别让槽长期双边

CLUSTER SETSLOT 的导入/迁出如果拉得很长,ASK 重定向会变成常态。生产步骤:

  1. 目标节点 SETSLOT <slot> IMPORTING <source_id>
  2. 源节点 SETSLOT <slot> MIGRATING <target_id>
  3. MIGRATE 按 key 搬数据,批量而不是一条一条走业务线程
  4. 两边都 SETSLOT <slot> NODE <target_id>

运维用 redis-cli --cluster reshard 时,把 --cluster-pipeline 开到 10 以上,减少往返。业务侧把命令超时从 50ms 临时提到 200ms,只覆盖迁移窗口。

对照一组 8 节点、单槽 20 万 key 的迁移:

策略 迁移耗时 窗口内 P99
逐 key MIGRATE 190s 86ms
pipeline=20 41s 27ms
先停写再搬 22s 业务不可写

先停写只适合凌晨维护。白天用 pipeline,并允许短暂 ASK。

拓扑刷新别依赖碰运气

启动节点必须多于 1 个

只配一个 startup_nodes,那个节点在迁移中若正好是迁出方且负载高,客户端启动就会失败。至少放 3 个不同机架的节点。

周期性刷新也要有:

python 复制代码
import threading
import time

def refresh_loop(client: RedisCluster):
    while True:
        try:
            client.cluster_nodes()
        except Exception:
            pass
        time.sleep(30)

threading.Thread(target=refresh_loop, args=(rc,), daemon=True).start()

有的驱动默认只在报错时刷新。迁移刚结束的几十秒里,旧槽表仍然会制造 MOVED。30 秒主动拉一次 CLUSTER NODES,能把这段尾巴切短。

禁止在应用里缓存 host:port 后绕过 Cluster 客户端直连。槽一迁,直连就成为永久错误。

总结

MOVED 是拓扑更新信号,不是可重试的瞬时错误。用 Cluster 客户端消化重定向,区分 ASK 与 MOVED,迁移用 pipeline 压缩窗口,启动节点放 3 个以上,并定期刷新槽表。扩容时错误率应表现为短暂 ASK,而不是持续 5xx。

相关推荐
萨小耶2 小时前
AI 面试答题链路源码分析:Redis 幂等、题目锁与状态机如何配合
人工智能·redis·面试
ZealSinger2 小时前
Boot4升后session_redis属性怎么改名
redis·springsession·boot4·升级踩坑
牧瀬クリスだ3 小时前
Redis五大数据类型全解析:从入门到实战
java·数据库·redis·后端·缓存
我不会插花弄玉3 小时前
5.Hash 【由浅入深-redis】
redis·算法·哈希算法
Java小生不才4 小时前
SPRING AI向量化
redis·spring ai
Lucis__15 小时前
Redis持久化机制解析
redis·缓存·操作系统·文件·非关系型数据库·1024程序员节
用户0942485680318 小时前
第7章:Redis有序集合 ZSet 实战:排行榜与延时任务
redis·架构·源码
码士集团小青19 小时前
面霸篇:高频 Java 基础问题(核心卷一)
linux·服务器·数据库·redis·缓存·1024程序员节
2301_322414280419 小时前
springboot农产品交易平台的设计与实现08055-计算机毕设 原创(免费领源码+部署教程+开发环境)
java·spring boot·redis·mysql·tomcat·maven·mybatis