redis-cli --pipe 不支持集群重定向,应使用 redis-cli --cluster load;后者预分片并按 slot 发送,避免 MOVED/ASK 错误,且需校验导入结果。

redis-cli --pipe 在集群模式下直接报错:MOVED / ASK 重定向
用 redis-cli --pipe 往 Redis 集群灌数据时,命令会随机打到某个节点,而该节点发现 key 不归自己管,就返回 MOVED 12345 10.0.1.5:6379 或 ASK 12345 10.0.1.6:6379,redis-cli --pipe 默认不处理这类重定向,直接退出并报错。
这不是数据格式问题,是协议层路由缺失——--pipe 模式跳过了 redis-cli 的交互式重定向逻辑,只做裸 TCP 发送。
- 别指望加
-c(cluster mode)参数能生效:它只对普通命令行交互有效,--pipe下被忽略 - 也不要用
redis-cli -c --pipe组合:CLI 会拒绝这种矛盾参数,报ERR: --pipe and -c cannot be used together - 真实场景中,错误日志里高频出现的其实是
Protocol error: expected '$', got 'M'—— 因为 MOVED 响应以M开头,不是 RESP 协议期望的$
必须用 redis-cli --cluster load 做预分片再 pipe
Redis 5+ 自带的 redis-cli --cluster load 工具才是集群批量导入的正解:它先读取所有 key,用 CRC16 算出 slot,再按节点分片生成多路 pipe 输入流,每路只发给归属节点。
关键不是“快”,而是“不越界”——绕开了客户端路由,靠服务端 slot 分布信息提前切流。
- 输入必须是 RESP 格式(不是 JSON 或 CSV),每行一个命令,如:
*3\r\n$3\r\nSET\r\n$5\r\nuser:1\r\n$3\r\nfoo\r\n - 执行前确保集群已稳定(
redis-cli --cluster check无 fail 状态),否则 load 会卡在等待 slot 映射同步 - 如果原始数据含大量 hash tag(如
{user1001}.profile),CRC16 计算仍正确,无需额外处理 - 性能上,单节点吞吐受 pipe 批量大小影响,默认 1000 条/批;可加
--batch-size 5000提升,但内存占用会上升
手写脚本做 client-side 分片时,slot 计算必须严格对齐 Redis 实现
如果不用 --cluster load,而选择自己解析 key、计算 slot 发往对应节点,最容易翻车的是 CRC16 实现偏差——Python 的 zlib.crc32 和 Redis C 的 crc16() 行为不一致。
Redis 的 slot = crc16(key) & 0x3FFF(即对 16384 取模),但很多脚本误用 hashlib.md5(key).hexdigest()[:4] 或 ord(key[0]) % 16384,结果 key 被发到错误节点,数据写丢或覆盖。
- Python 正确做法:用
redis-py内置的redis.cluster.RedisCluster._keyslot(),或抄 Redis 源码里的crc16.c的 Python 移植版 - Node.js 推荐用
ioredis的Cluster.prototype.computeSlot(),别自己写位运算 - 注意大 key(>1MB)在 pipe 中可能触发
proto-max-bulk-len限制,需同步调大目标节点配置
PIPE 导入后必须 verify:集群 slot 状态和数据一致性不自动校验
redis-cli --cluster load 成功返回 ≠ 数据已到位。它只保证命令发出去了,不检查响应是否成功、是否被丢弃、是否因 OOM 被拒绝。
尤其当集群内存不足、maxmemory-policy 是 noeviction 以外策略时,SET 可能静默失败,但 pipe 流程照常结束。
- 务必跑一次
redis-cli --cluster check,确认所有 slot 的keys数量与预期导入量匹配 - 抽样验证:用
redis-cli -h node1 -p 6379 keys "user:*" | wc -l对比各节点,避免某节点因网络抖动漏收整批 - 如果用了 hash tag,要额外验证 tag 内 key 是否真落在同一 slot —— 错误的 tag 写法(如
user:{1001}少了闭合括号)会导致 slot 计算完全错乱
最麻烦的不是导入慢,是导入完你以为完成了,结果查不到数据,回头还得从 pipe 日志里 grep ERROR 行——而默认 pipe 不输出每条命令的响应,得手动加 --verbose 或改用 telnet 抓包看。









