根本原因是网络往返(RTT)开销远大于命令执行时间;Pipeline通过客户端批量发送、服务端顺序执行来减少RTT,但无原子性;Lua脚本则提供单线程原子执行,适用于有逻辑依赖或条件判断的场景。

连续执行单条 Redis 命令慢,根本原因是网络往返(RTT)开销远大于命令本身执行时间。每发一条 GET、INCR 或 DEL,都要等一次 TCP 往返 —— 即使 Redis 本地跑在本机 Docker 里,loopback 也要耗 0.1~0.3ms;公网延迟动辄几毫秒到几十毫秒。100 次单命令 = 100 次 RTT;改用批量方式,通常压到 1 次 RTT 就能解决大半性能问题。
为什么 Pipeline 比单命令快得多
Pipeline 不是 Redis 服务端的新功能,而是客户端把多条命令攒起来一次性发过去,Redis 收到后顺序执行、再打包返回。它不改变命令语义,也不提供原子性,但直接砍掉 99% 的网络等待时间。
- 必须用支持 pipeline 的客户端库,比如 Python 的
redis-py要显式调用pipeline()方法,不能只写r.get("k1"); r.get("k2") - 注意
execute()才真正发包 —— 之前所有命令只是缓存在客户端内存里,没走网络 - 错误不会中断后续命令:比如第 3 条是
INCR一个 string 类型的 key,报(error) ERR value is not an integer or out of range,第 4~10 条仍会执行 - 返回结果是列表,顺序严格对应发送顺序,需手动按索引检查每个
result[i]是否为None或异常对象
什么时候该选 Lua 脚本而不是 Pipeline
当多个操作之间有逻辑依赖、或需要条件判断、或必须原子执行时,Pipeline 不顶用 —— 它只是“批量发”,不是“批量原子执行”。这时得上 EVAL 或 EVALSHA。
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
- Lua 脚本在 Redis 单线程内完整执行,中间不会被其他客户端打断,天然原子
- 适合场景:先
GET再INCRBY再SET;或者“如果 key 不存在才 SET”这类SETNX+EXPIRE组合 - 脚本内容过长会拖慢网络传输,建议用
SCRIPT LOAD预加载,之后用EVALSHA执行 hash,避免重复传脚本体 - 注意
redis.call()抛错会终止整个脚本,而redis.pcall()可捕获错误继续执行 —— 选哪个取决于你是否允许部分失败
Pipeline 和 Lua 能不能一起用
能,但意义不大。Pipeline 是客户端行为,Lua 是服务端行为,两者叠加 ≠ 双倍加速,反而容易引入混淆。
- 常见误区:用 pipeline 包裹多个
EVAL调用 —— 这只是把 N 个 Lua 调用批量发过去,每个 Lua 仍是独立执行、独立加锁、独立占用 CPU,RTT 省了,但 Redis 单线程瓶颈还在 - 真正高效的做法是:把原本要 pipeline 的 N 个简单命令,合并进一个 Lua 脚本里执行,让逻辑收敛到一次
EVAL - 例外情况:你需要混合执行“纯数据操作”(如
MGET)和“带逻辑的 Lua”,这时可 pipeline 包一层MGET+ 一层EVAL,但得自己处理响应顺序对齐
最容易被忽略的一点:别为了“看起来批量”而硬套 Pipeline 或 Lua。比如只查 2 个 key,用 MGET 就够了;更新哈希表 5 个字段,直接 HMSET 比写 Lua 更轻量。优先用原生命令,其次 Pipeline,最后才是 Lua —— 复杂度和可维护性会上升一个量级。

















