redis pipeline可将N个命令合并为1次RTT,大幅提升吞吐量,但不保证原子性;超大pipeline需分批(≤5k)防OOM;逻辑依赖场景应使用lua脚本,且KEYS必须显式声明、同槽位,避免CROSSSLOT错误。

用 pipeline 批量发命令,别一个个 set / get
单次网络往返(RTT)执行一个命令,10 个命令就是 10 次 RTT —— 这是最大瓶颈。Redis 的 pipeline 把多个命令打包成一次请求发送,服务端顺序执行后统一回包,RTT 直接从 N 次降到 1 次。
常见错误是以为 pipeline 自动事务化:它不保证原子性,中间某个命令失败,后续仍会执行;也不做命令间依赖校验(比如 get 结果拿不到,后面还硬塞 incr)。
- Python redis-py 示例:
pipe = r.pipeline() pipe.set('a', 1) pipe.get('a') pipe.incr('b') result = pipe.execute() # 返回 ['OK', '1', 1] - Java Jedis 同理,调用
pipelined()获取Pipeline对象 - 注意:
pipeline缓存命令在客户端内存,超大 pipeline(如 10w+ 命令)可能 OOM,建议分批,每批 ≤5k
lua 脚本替代多命令组合,尤其涉及条件或中间值
当多个命令之间有逻辑依赖(比如“如果 key 不存在才 set”,或“取值 → 计算 → 写回”),pipeline 无能为力,必须用 eval 或 evalsha 执行 Lua 脚本——整个脚本在 Redis 单线程内原子执行,中间值不落地、无竞态。
容易踩的坑是脚本里硬编码 key 名,导致无法被 Redis Cluster 路由(key 必须显式声明在 KEYS 数组里);还有脚本超时(默认 5 秒),死循环或大表遍历直接触发 BUSY 错误。
- 正确写法(Python):
r.eval("if redis.call('exists', KEYS[1]) == 0 then return redis.call('set', KEYS[1], ARGV[1]) else return 0 end", 1, 'mykey', 'myval') - 所有 key 必须通过
KEYS传入,不能字符串拼接;非 key 数据走ARGV - 本地调试用
redis-cli --eval,上线前先script load得到 sha1,再用evalsha提升性能
避免在 pipeline 里混用读写命令导致连接阻塞
某些客户端(如旧版 jedis)在 pipeline 中遇到 get 等读命令时,会等待响应才发下一条,实际退化成串行——看起来用了 pipeline,实则没省 RTT。
根本原因是客户端对 pipeline 的实现差异:有的只缓存命令不缓存响应,有的则按“发一批→等全部回包”设计。更隐蔽的问题是,pipeline 中夹杂 auth、select 这类连接级命令,会导致后续命令路由错乱或认证失败。
- 检查客户端文档是否明确支持“异步 pipeline”或“non-blocking pipeline”
- 禁止在 pipeline 中出现
auth、select、swapdb;数据库切换、认证必须在 pipeline 外完成 - 读写分离场景下,确保 pipeline 全部发往同一节点(比如只写主库,或只读从库),别跨角色混用
集群环境下 pipeline 和 lua 的 key 分片约束
Redis Cluster 要求 pipeline 中所有 key 必须落在同一个哈希槽,否则报 CROSSSLOT 错误;Lua 脚本同理,KEYS 数组里的 key 也必须同槽——这是最常被忽略的硬限制。
不是所有多 key 操作都能无脑上 pipeline 或 lua。比如想批量操作用户订单(order:123、order:456),但它们 hash tag 不一致,就天然跨槽。
- 解决方案只有两个:
{user_id}这种 hash tag 强制同槽,或改用单 key + 内部结构(如用hset order_data 123 json...把多个订单塞进一个 hash) - Cluster 下
evalsha必须确保脚本已预加载到所有节点(或至少目标节点),否则 fallback 到eval会失败 - 测试阶段用
redis-cli -c连集群,手动cluster keyslot your_key查槽位,比猜靠谱
真正卡住效率的往往不是命令本身,而是 key 设计是否适配 pipeline/lua 的约束,以及客户端是否真把命令攒够了再发——这两点比选函数更重要。