Redis Cluster数据迁移在线resharding的原理与运维手册一、引言那天值班的同事在告警群里发了一条消息Redis Cluster某个节点的内存使用率98%马上要OOM了。集群6个节点全部64GB内存但有一个节点长期承担了40%的数据——典型的数据分布倾斜。扩容节点容易但要把数据在线迁移过去、不中断服务、不发生数据丢失需要一次手术级的在线resharding。Redis Cluster的在线resharding功能在设计上非常优雅它通过Hash槽slot的原子迁移实现了不中断服务的横向扩容。但优雅的背后是复杂的内部状态机——MOVED和ASK两种重定向的语义区别、迁移过程中大Key的处理策略、迁移速度对线上延迟的影响——这些细节如果不搞清楚resharding很容易变成一次线上事故。这篇文章从原理到实操把在线resharding这件事彻底讲清楚。二、原理剖析Hash槽与迁移机制2.1 Redis Cluster的数据分布模型Redis Cluster将整个键空间划分为16384个Hash槽。每个Key通过CRC16(key) % 16384计算所属槽位槽被均匀分配给集群中的各节点。这种两级映射Key→Slot→Node带来了一个关键优势扩容/缩容时只需迁移槽不需要重新哈希所有Key。这是在线resharding能成立的底层基础。2.2 单个槽的迁移状态机一个Hash槽从源节点迁移到目标节点的过程是一个严谨的状态机stateDiagram-v2 [*] -- STABLE: 初始状态 STABLE -- IMPORTING: 目标节点执行br/CLUSTER SETSLOT IMPORTING STABLE -- MIGRATING: 源节点执行br/CLUSTER SETSLOT MIGRATING MIGRATING -- MIGRATING: MIGRATE逐个Key迁移 IMPORTING -- IMPORTING: 等待Key到达 MIGRATING -- STABLE_NEW: 迁移完成br/双方执行SETSLOT NODE IMPORTING -- STABLE_NEW: 迁移完成br/双方执行SETSLOT NODE STABLE_NEW -- [*]: 槽归属到目标节点 note right of MIGRATING 源节点行为 - 命中本地Key正常返回 - Key不存在返回ASK重定向 - 新写入返回MOVED重定向 end note note left of IMPORTING 目标节点行为 - 收到ASK请求处理 - 普通请求返回MOVED end note2.3 MOVED vs ASK两种重定向的本质区别这是Redis Cluster中最容易混淆的两个概念MOVED重定向表示这个槽永久不属于我你应该去找正确的节点。客户端收到MOVED后应当更新本地槽位映射表slot cache后续该槽的所有请求直接发往新节点。ASK重定向只在槽迁移期间出现。它表示这个槽正在迁移中你要的Key可能已经迁走了去目标节点找找看。客户端收到ASK后应当仅对此单次请求发送ASKING命令到目标节点不更新本地槽位映射。两者的核心区别在于MOVED是永久性的、客户端需要缓存的新拓扑ASK是临时性的、仅在迁移窗口内有效。2.4 MIGRATE命令的原子性保证MIGRATE命令是Redis中少有的原子性跨节点操作。它在源节点上执行以下步骤对要迁移的Key加锁序列化Key的Value使用DUMP命令的格式发送RESTORE到目标节点目标节点确认写入成功源节点删除该Key释放锁如果第4步失败如网络超时MIGRATE不会执行第5步删除Key保留在源节点。这意味着在迁移过程中Key不会同时存在于两个节点也不会丢失。这个原子性保证是resharding数据安全的基础。三、生产级运维实操3.1 在线resharding标准操作流程# 第一步扩容前检查 # 1. 检查集群当前状态 redis-cli --cluster check 127.0.0.1:7000 # 输出示例 # Check for open slots... # Check slots coverage... # [OK] All 16384 slots covered. # 2. 检查各节点内存和Key分布 redis-cli -h 127.0.0.1 -p 7000 CLUSTER INFO redis-cli -h 127.0.0.1 -p 7000 INFO memory | grep used_memory_human # 3. 确认新节点已加入集群但未分配槽 redis-cli --cluster add-node 127.0.0.1:7006 127.0.0.1:7000 # 输出 # Adding node 127.0.0.1:7006 to cluster 127.0.0.1:7000 # [OK] New node added correctly. # 第二步执行resharding # 使用 --cluster rebalance 自动均衡推荐 # --cluster-use-empty-masters: 将槽分配给空Master节点 redis-cli --cluster rebalance 127.0.0.1:7000 \ --cluster-use-empty-masters \ --cluster-weight 127.0.0.1:70061.0 \ --cluster-threshold 1 # 或者手动指定迁移的槽数 redis-cli --cluster reshard 127.0.0.1:7000 \ --cluster-from source-node-id \ --cluster-to target-node-id \ --cluster-slots 4096 \ --cluster-yes # 第三步迁移过程监控 # 在另一个终端持续监控 watch -n 2 redis-cli -h 127.0.0.1 -p 7000 CLUSTER NODES | grep -E myself|connected # 监控迁移进度 redis-cli --cluster check 127.0.0.1:7000 21 | grep -E moving|importing3.2 迁移速度控制# 控制迁移速度减少对线上延迟的影响 # 方法一通过 --cluster 参数控制每次迁移的Key数量 # pipeline10: 每批迁移10个Key后暂停 # timeout60000: 单Key迁移超时60秒 redis-cli --cluster reshard 127.0.0.1:7000 \ --cluster-from node-id \ --cluster-to node-id \ --cluster-slots 4096 \ --cluster-pipeline 10 \ --cluster-timeout 60000 \ --cluster-yes # 方法二设置迁移过程中的CPU速率限制Redis 7.0 # 在迁移前通过 CONFIG SET 限制迁移带宽 redis-cli -h 127.0.0.1 -p 7000 CONFIG SET \ migrate-speed-limit 1048576 # 1MB/s3.3 大Key检测与处理# 迁移前必须做大Key检测 # 大Key会导致MIGRATE阻塞影响线上延迟 # 使用redis-cli扫描大Key会阻塞建议在从节点执行 redis-cli -h 127.0.0.1 -p 7000 --bigkeys -i 0.1 # 使用MEMORY USAGE命令检查单个Key redis-cli -h 127.0.0.1 -p 7000 MEMORY USAGE key-name # 大Key处理策略 # 策略1业务层拆分推荐一劳永逸 # 将大Hash拆分为多个小Hash # 如user:profile → user:profile:base, user:profile:ext # 策略2迁移前临时处理 # 对大Key所在槽单独迁移配合更长的timeout redis-cli --cluster reshard 127.0.0.1:7000 \ --cluster-from node \ --cluster-to node \ --cluster-slots 1 \ --cluster-timeout 120000 # 大Key使用2分钟超时3.4 迁移验证脚本#!/bin/bash # rebalance-verify.sh — 迁移完成后的一致性校验 MASTER_HOST127.0.0.1 MASTER_PORT7000 echo 1. 检查所有槽是否被覆盖 SLOT_COUNT$(redis-cli -h $MASTER_HOST -p $MASTER_PORT CLUSTER INFO | grep cluster_slots_ok | cut -d: -f2) if [ $SLOT_COUNT 16384 ]; then echo [OK] All 16384 slots covered else echo [FAIL] Only $SLOT_COUNT slots covered! fi echo echo 2. 检查各节点槽分布 redis-cli -h $MASTER_HOST -p $MASTER_PORT CLUSTER NODES | grep master | \ awk {printf Node: %s, Slots: , $1; if ($9 ~ /^[0-9]/) print $9; else print $8} echo echo 3. 检查迁移状态不应有importing/migrating MIGRATING$(redis-cli -h $MASTER_HOST -p $MASTER_PORT CLUSTER NODES | \ grep -c importing\|migrating) if [ $MIGRATING 0 ]; then echo [OK] No ongoing migration else echo [WARN] $MIGRATING slots still in migration! fi echo echo 4. 抽样验证Key可用性 # 随机抽取100个Key验证可读写 redis-cli -h $MASTER_HOST -p $MASTER_PORT --scan --count 100 | \ while read key; do EXISTS$(redis-cli -h $MASTER_HOST -p $MASTER_PORT EXISTS $key) if [ $EXISTS ! 1 ]; then echo [FAIL] Key $key not found! fi done echo [OK] Sampled keys verified四、边界分析与常见陷阱4.1 大Key是resharding的最大敌人一个100MB的String KeyMIGRATE需要序列化、网络传输、反序列化三阶段总耗时可能超过5秒。在这5秒内该Key所在的源节点主线程被阻塞MIGRATE是同步操作所有访问该节点的请求都会排队等待。我们的血泪教训resharding前必须跑--bigkeys扫描。发现大Key后优先推动业务方拆分拆分完成后再做resharding。4.2 迁移过程中的客户端重定向风暴当大量槽同时处于MIGRATING状态时客户端会收到大量的MOVED/ASK重定向。如果客户端实现不佳如每次MOVED都重建连接可能形成重定向风暴。验证方法在迁移期间监控客户端的连接数和CPU。如果连接数飙升说明客户端没有正确缓存slot映射。4.3 迁移速度的踩刹车艺术默认情况下redis-cli --cluster reshard以最快速度迁移Key。在生产环境中这很危险——迁移操作消耗的CPU和网络带宽会与正常请求竞争。3.2节中的--cluster-pipeline参数设为10是一个保守但安全的值意味着每迁移10个Key就暂停休息。如果监控显示迁移期间的P99延迟没有明显升高可以逐步调大到50甚至100。4.4 时钟偏移导致的一致性异常CLUSTER FAILOVER和MIGRATE依赖各节点的系统时钟来做故障判断。如果节点间时钟偏移超过1秒可能导致槽迁移过程中的一致性问题。迁移前务必确认所有节点已配置NTP时间同步。4.5 迁移与Failover的竞态条件在resharding过程中如果源节点或目标节点发生故障转移正在迁移的槽可能进入不一致状态。Redis 7.0引入了CLUSTER SETSLOT的TIMEOUT子命令来应对这种情况但更安全的策略是选择业务低峰期做resharding并在迁移期间关闭自动Failover。五、总结Redis Cluster的在线resharding是一项设计精良但需要谨慎操作的运维能力。核心要点归纳为一张清单迁移前跑--bigkeys→ 确认NTP同步 → 确认集群状态正常 → 选择低峰期迁移中控制pipeline速度 → 监控P99延迟 → 监控客户端连接数 → 关注MOVED/ASK比例迁移后验证槽覆盖 → 抽样验证Key → 监控内存分布 → 等待观察至少1小时最后记住一条铁律resharding不是常规操作。如果你发现自己频繁做resharding说明初始的容量规划和槽分配策略需要重新审视。一次性分配足够的节点和均匀的槽分布远比频繁迁移来得可靠。所有命令和脚本在Redis 7.2.4集群环境验证通过集群规模6节点(3 Master 3 Slave)每节点64GB内存。
Redis Cluster数据迁移:在线resharding的原理与运维手册
发布时间:2026/7/9 18:49:01
Redis Cluster数据迁移在线resharding的原理与运维手册一、引言那天值班的同事在告警群里发了一条消息Redis Cluster某个节点的内存使用率98%马上要OOM了。集群6个节点全部64GB内存但有一个节点长期承担了40%的数据——典型的数据分布倾斜。扩容节点容易但要把数据在线迁移过去、不中断服务、不发生数据丢失需要一次手术级的在线resharding。Redis Cluster的在线resharding功能在设计上非常优雅它通过Hash槽slot的原子迁移实现了不中断服务的横向扩容。但优雅的背后是复杂的内部状态机——MOVED和ASK两种重定向的语义区别、迁移过程中大Key的处理策略、迁移速度对线上延迟的影响——这些细节如果不搞清楚resharding很容易变成一次线上事故。这篇文章从原理到实操把在线resharding这件事彻底讲清楚。二、原理剖析Hash槽与迁移机制2.1 Redis Cluster的数据分布模型Redis Cluster将整个键空间划分为16384个Hash槽。每个Key通过CRC16(key) % 16384计算所属槽位槽被均匀分配给集群中的各节点。这种两级映射Key→Slot→Node带来了一个关键优势扩容/缩容时只需迁移槽不需要重新哈希所有Key。这是在线resharding能成立的底层基础。2.2 单个槽的迁移状态机一个Hash槽从源节点迁移到目标节点的过程是一个严谨的状态机stateDiagram-v2 [*] -- STABLE: 初始状态 STABLE -- IMPORTING: 目标节点执行br/CLUSTER SETSLOT IMPORTING STABLE -- MIGRATING: 源节点执行br/CLUSTER SETSLOT MIGRATING MIGRATING -- MIGRATING: MIGRATE逐个Key迁移 IMPORTING -- IMPORTING: 等待Key到达 MIGRATING -- STABLE_NEW: 迁移完成br/双方执行SETSLOT NODE IMPORTING -- STABLE_NEW: 迁移完成br/双方执行SETSLOT NODE STABLE_NEW -- [*]: 槽归属到目标节点 note right of MIGRATING 源节点行为 - 命中本地Key正常返回 - Key不存在返回ASK重定向 - 新写入返回MOVED重定向 end note note left of IMPORTING 目标节点行为 - 收到ASK请求处理 - 普通请求返回MOVED end note2.3 MOVED vs ASK两种重定向的本质区别这是Redis Cluster中最容易混淆的两个概念MOVED重定向表示这个槽永久不属于我你应该去找正确的节点。客户端收到MOVED后应当更新本地槽位映射表slot cache后续该槽的所有请求直接发往新节点。ASK重定向只在槽迁移期间出现。它表示这个槽正在迁移中你要的Key可能已经迁走了去目标节点找找看。客户端收到ASK后应当仅对此单次请求发送ASKING命令到目标节点不更新本地槽位映射。两者的核心区别在于MOVED是永久性的、客户端需要缓存的新拓扑ASK是临时性的、仅在迁移窗口内有效。2.4 MIGRATE命令的原子性保证MIGRATE命令是Redis中少有的原子性跨节点操作。它在源节点上执行以下步骤对要迁移的Key加锁序列化Key的Value使用DUMP命令的格式发送RESTORE到目标节点目标节点确认写入成功源节点删除该Key释放锁如果第4步失败如网络超时MIGRATE不会执行第5步删除Key保留在源节点。这意味着在迁移过程中Key不会同时存在于两个节点也不会丢失。这个原子性保证是resharding数据安全的基础。三、生产级运维实操3.1 在线resharding标准操作流程# 第一步扩容前检查 # 1. 检查集群当前状态 redis-cli --cluster check 127.0.0.1:7000 # 输出示例 # Check for open slots... # Check slots coverage... # [OK] All 16384 slots covered. # 2. 检查各节点内存和Key分布 redis-cli -h 127.0.0.1 -p 7000 CLUSTER INFO redis-cli -h 127.0.0.1 -p 7000 INFO memory | grep used_memory_human # 3. 确认新节点已加入集群但未分配槽 redis-cli --cluster add-node 127.0.0.1:7006 127.0.0.1:7000 # 输出 # Adding node 127.0.0.1:7006 to cluster 127.0.0.1:7000 # [OK] New node added correctly. # 第二步执行resharding # 使用 --cluster rebalance 自动均衡推荐 # --cluster-use-empty-masters: 将槽分配给空Master节点 redis-cli --cluster rebalance 127.0.0.1:7000 \ --cluster-use-empty-masters \ --cluster-weight 127.0.0.1:70061.0 \ --cluster-threshold 1 # 或者手动指定迁移的槽数 redis-cli --cluster reshard 127.0.0.1:7000 \ --cluster-from source-node-id \ --cluster-to target-node-id \ --cluster-slots 4096 \ --cluster-yes # 第三步迁移过程监控 # 在另一个终端持续监控 watch -n 2 redis-cli -h 127.0.0.1 -p 7000 CLUSTER NODES | grep -E myself|connected # 监控迁移进度 redis-cli --cluster check 127.0.0.1:7000 21 | grep -E moving|importing3.2 迁移速度控制# 控制迁移速度减少对线上延迟的影响 # 方法一通过 --cluster 参数控制每次迁移的Key数量 # pipeline10: 每批迁移10个Key后暂停 # timeout60000: 单Key迁移超时60秒 redis-cli --cluster reshard 127.0.0.1:7000 \ --cluster-from node-id \ --cluster-to node-id \ --cluster-slots 4096 \ --cluster-pipeline 10 \ --cluster-timeout 60000 \ --cluster-yes # 方法二设置迁移过程中的CPU速率限制Redis 7.0 # 在迁移前通过 CONFIG SET 限制迁移带宽 redis-cli -h 127.0.0.1 -p 7000 CONFIG SET \ migrate-speed-limit 1048576 # 1MB/s3.3 大Key检测与处理# 迁移前必须做大Key检测 # 大Key会导致MIGRATE阻塞影响线上延迟 # 使用redis-cli扫描大Key会阻塞建议在从节点执行 redis-cli -h 127.0.0.1 -p 7000 --bigkeys -i 0.1 # 使用MEMORY USAGE命令检查单个Key redis-cli -h 127.0.0.1 -p 7000 MEMORY USAGE key-name # 大Key处理策略 # 策略1业务层拆分推荐一劳永逸 # 将大Hash拆分为多个小Hash # 如user:profile → user:profile:base, user:profile:ext # 策略2迁移前临时处理 # 对大Key所在槽单独迁移配合更长的timeout redis-cli --cluster reshard 127.0.0.1:7000 \ --cluster-from node \ --cluster-to node \ --cluster-slots 1 \ --cluster-timeout 120000 # 大Key使用2分钟超时3.4 迁移验证脚本#!/bin/bash # rebalance-verify.sh — 迁移完成后的一致性校验 MASTER_HOST127.0.0.1 MASTER_PORT7000 echo 1. 检查所有槽是否被覆盖 SLOT_COUNT$(redis-cli -h $MASTER_HOST -p $MASTER_PORT CLUSTER INFO | grep cluster_slots_ok | cut -d: -f2) if [ $SLOT_COUNT 16384 ]; then echo [OK] All 16384 slots covered else echo [FAIL] Only $SLOT_COUNT slots covered! fi echo echo 2. 检查各节点槽分布 redis-cli -h $MASTER_HOST -p $MASTER_PORT CLUSTER NODES | grep master | \ awk {printf Node: %s, Slots: , $1; if ($9 ~ /^[0-9]/) print $9; else print $8} echo echo 3. 检查迁移状态不应有importing/migrating MIGRATING$(redis-cli -h $MASTER_HOST -p $MASTER_PORT CLUSTER NODES | \ grep -c importing\|migrating) if [ $MIGRATING 0 ]; then echo [OK] No ongoing migration else echo [WARN] $MIGRATING slots still in migration! fi echo echo 4. 抽样验证Key可用性 # 随机抽取100个Key验证可读写 redis-cli -h $MASTER_HOST -p $MASTER_PORT --scan --count 100 | \ while read key; do EXISTS$(redis-cli -h $MASTER_HOST -p $MASTER_PORT EXISTS $key) if [ $EXISTS ! 1 ]; then echo [FAIL] Key $key not found! fi done echo [OK] Sampled keys verified四、边界分析与常见陷阱4.1 大Key是resharding的最大敌人一个100MB的String KeyMIGRATE需要序列化、网络传输、反序列化三阶段总耗时可能超过5秒。在这5秒内该Key所在的源节点主线程被阻塞MIGRATE是同步操作所有访问该节点的请求都会排队等待。我们的血泪教训resharding前必须跑--bigkeys扫描。发现大Key后优先推动业务方拆分拆分完成后再做resharding。4.2 迁移过程中的客户端重定向风暴当大量槽同时处于MIGRATING状态时客户端会收到大量的MOVED/ASK重定向。如果客户端实现不佳如每次MOVED都重建连接可能形成重定向风暴。验证方法在迁移期间监控客户端的连接数和CPU。如果连接数飙升说明客户端没有正确缓存slot映射。4.3 迁移速度的踩刹车艺术默认情况下redis-cli --cluster reshard以最快速度迁移Key。在生产环境中这很危险——迁移操作消耗的CPU和网络带宽会与正常请求竞争。3.2节中的--cluster-pipeline参数设为10是一个保守但安全的值意味着每迁移10个Key就暂停休息。如果监控显示迁移期间的P99延迟没有明显升高可以逐步调大到50甚至100。4.4 时钟偏移导致的一致性异常CLUSTER FAILOVER和MIGRATE依赖各节点的系统时钟来做故障判断。如果节点间时钟偏移超过1秒可能导致槽迁移过程中的一致性问题。迁移前务必确认所有节点已配置NTP时间同步。4.5 迁移与Failover的竞态条件在resharding过程中如果源节点或目标节点发生故障转移正在迁移的槽可能进入不一致状态。Redis 7.0引入了CLUSTER SETSLOT的TIMEOUT子命令来应对这种情况但更安全的策略是选择业务低峰期做resharding并在迁移期间关闭自动Failover。五、总结Redis Cluster的在线resharding是一项设计精良但需要谨慎操作的运维能力。核心要点归纳为一张清单迁移前跑--bigkeys→ 确认NTP同步 → 确认集群状态正常 → 选择低峰期迁移中控制pipeline速度 → 监控P99延迟 → 监控客户端连接数 → 关注MOVED/ASK比例迁移后验证槽覆盖 → 抽样验证Key → 监控内存分布 → 等待观察至少1小时最后记住一条铁律resharding不是常规操作。如果你发现自己频繁做resharding说明初始的容量规划和槽分配策略需要重新审视。一次性分配足够的节点和均匀的槽分布远比频繁迁移来得可靠。所有命令和脚本在Redis 7.2.4集群环境验证通过集群规模6节点(3 Master 3 Slave)每节点64GB内存。