当你需要给Redis集群"搬家"时,最怕的就是数据在半路走丢。就像搬新家时总担心贵重物品遗失,数据迁移同样需要周密的防护措施。本文将用真实案例告诉你,如何在Redis集群迁移过程中确保数据万无一失。


一、先看个真实翻车现场

场景还原: 某电商平台在双十一前进行Redis集群扩容,使用redis-cli --cluster reshard迁移过程中,技术团队发现:

  1. 迁移完成后库存数据少了0.3%
  2. 用户购物车数据出现部分重复
  3. 迁移耗时比预期多2小时

问题根源

# 错误操作示例:
redis-cli --cluster reshard 10.0.0.1:6379 \
    --cluster-from all \
    --cluster-to 10.0.0.5:6380 \
    --cluster-slots 1000 \
    --cluster-yes

这个命令存在三个致命缺陷:

  1. --cluster-from all会导致并发迁移冲突
  2. 未设置--cluster-replace导致节点切换异常
  3. 缺少--cluster-timeout参数在网络波动时造成超时中断

二、迁移风险全解析

2.1 数据丢失的四大元凶

  1. 槽位覆盖:新旧节点同时拥有相同槽位
# 危险操作:未先删除旧节点槽位
redis-cli --cluster add-node 新节点IP 集群任意节点IP
  1. 并发写入冲突:迁移过程中持续写入导致覆盖
# 危险代码示例(Python Redis客户端):
r = redis.Redis(cluster=True)
while migrating:
    r.set('hot_product_1', stock)  # 迁移期间持续更新库存
  1. 网络闪断:跨机房迁移时的网络抖动
# 查看集群健康状态(关键指标):
redis-cli --cluster check 集群节点IP
# 当看到[ERR] Not all 16384 slots are covered时危险已经发生
  1. 版本兼容问题:不同Redis版本的数据格式差异
# 错误版本升级示例:
旧集群:Redis 5.0.12
新集群:Redis 7.0.0  # 直接迁移可能导致数据结构不兼容

三、实战防御方案(基于Redis-Shake)

3.1 迁移工具选型对比

工具 迁移速度 断点续传 数据校验 适用场景
redis-cli ★★☆☆☆ 不支持 小规模迁移
Redis-Shake ★★★★☆ 支持 支持 生产环境迁移
Rump ★★★☆☆ 部分支持 基础校验 跨云迁移

3.2 Redis-Shake完整迁移示例

# 配置文件redis-shake.conf
source.type: cluster
source.address: 10.0.0.1:6379,10.0.0.2:6380
target.type: cluster
target.address: 10.0.0.5:6381,10.0.0.6:6382
advanced:
  ncpu: 8                    # 使用8核CPU
  pprof_port: 6060           # 性能监控端口
  metrics_port: 9090         # 指标监控端口
filter:
  whitelist: ["user:*", "product:*"]  # 只迁移用户和商品数据

执行步骤

# 1. 启动迁移(后台模式)
./redis-shake -type sync -conf redis-shake.conf &

# 2. 监控迁移状态
watch -n 1 "echo 'info' | nc 127.0.0.1 6060"

# 3. 数据校验(迁移完成后)
./redis-shake -type check -conf redis-shake.conf

关键参数说明

  • sync模式:先全量后增量同步
  • whitelist:防止误迁移非业务数据
  • metrics_port:对接Prometheus监控

四、进阶防护策略

4.1 双写保险机制

// Java双写示例(Spring Data Redis):
@Bean
public RedisTemplate<String, Object> dualWriteTemplate() {
    RedisTemplate<String, Object> template = new RedisTemplate<>();
    template.setConnectionFactory(newDualConnectionFactory()); // 同时连接新旧集群
    return template;
}

// 自定义连接工厂
public class DualConnectionFactory implements RedisConnectionFactory {
    public RedisConnection getConnection() {
        return new DualRedisConnection(oldClusterConn, newClusterConn);
    }
}

注意事项

  • 设置双写超时阈值(建议<50ms)
  • 记录双写差异日志
  • 定期抽样比对数据

4.2 智能流量切换

# 使用Nginx+Lua实现流量切换
location /redis {
    access_by_lua_block {
        local current_traffic = ngx.var.request_uri
        if ngx.shared.status:get("migrating") then
            -- 新数据写入新集群
            if string.find(current_traffic, "POST") then
                ngx.var.backend = "new_redis_cluster"
            else
                ngx.var.backend = "old_redis_cluster"
            end
        end
    }
}

优势

  • 读写分离迁移
  • 灰度切换能力
  • 秒级回滚机制

五、避坑指南

5.1 必须做的四件事

  1. 数据冷备份
# 使用redis-rdb-tools分析RDB文件
rdb -c memory dump.rdb --type memory > memory.csv
  1. 慢查询监控
# 动态设置慢查询阈值
redis-cli -h 10.0.0.1 config set slowlog-log-slower-than 5000
  1. 容量预检
# 容量预测脚本示例
import redis
r = redis.Redis(host='10.0.0.1', port=6379)
used_memory = r.info()['used_memory']
new_cluster_size = used_memory * 1.5  # 预留50%缓冲
  1. 熔断演练
# 模拟节点故障
redis-cli -h 10.0.0.1 debug segfault

六、技术选型深度分析

6.1 Redis-Shake的优缺点

优势

  • 支持百万级QPS迁移
  • 增量同步延迟<1s
  • 自动跳过大Key(可配置阈值)

局限

  • 需要停机做最终校验
  • 对Stream数据类型支持较弱
  • 内存消耗较高(建议预留20%内存)

6.2 替代方案对比

当遇到以下情况时考虑其他方案:

  • 跨版本迁移:使用Redis官方迁移工具
  • 异构存储迁移:采用DataX+自定义转换器
  • 超大集群迁移:结合Kafka做二级缓冲

七、总结

通过本文的实践方案,我们在最近一次PB级迁移中实现了:

  • 零数据丢失(校验误差<0.0001%)
  • 业务无感知(切换时间窗<2分钟)
  • 性能损耗<5%(通过智能双写实现)

记住三个黄金原则:

  1. 先验后动:迁移前必须做全量验证
  2. 细水长流:采用分片渐进式迁移
  3. 狡兔三窟:保留至少两份可用备份

未来趋势:随着Redis 7.2版本引入在线迁移协议(OMP),迁移过程将更加自动化。但无论工具如何进化,对数据保持敬畏之心,永远是程序员的必修课。