908 字
约 3 分钟
8
S12-J04 · 事务、Lua 与 Pipeline

适用读者:以为 MULTI 能回滚、或把 Pipeline 当事务的人。
前置:S12-B01。
实测环境:Redis 7.0.15(2026-11-05)。
覆盖旧文:876(86) 1572(77) 1570(35) = 198 点。
覆盖扩展:E-S12-013(三者边界对比)。
上一篇:S12-J03 · 分布式锁:SETNX → Redisson → RedLock
下一篇:S12-J05 · 高级用法。


1. 一句话结论

三者保证的都是"不被打断",不是"可回滚"。MULTI/EXEC 出错不撤销、Lua 出错也不撤销——Redis 从来没有回滚。

实测(同一个错误,两种方式):

① MULTI ... SET k_a 1 / INCR s1(字符串) / SET k_b 2 ... EXEC
   → k_a=1  k_b=2      ← 两条 SET 仍生效,只有 INCR 失败

② EVAL "set k_c 1; incr s1" ...
   → ERR value is not an integer   且 k_c=1   ← set 已生效

2. 三者的定位

机制 原子性 回滚 用途
MULTI/EXEC 连续执行不被打断 ❌ 多条命令成组
Lua 脚本 整个脚本不被其他命令插入 ❌ 读-改-写(如锁校验、限流)
Pipeline ❌(只是批量发送) ❌ 减少 RTT,不是原子

判据:

要"读一个值再根据它写" → Lua(真正的读-改-写原子)
只是"多条独立命令一起发" → Pipeline(快)
要"要么全成功" → ❌ Redis 做不到(用数据库事务)

3. 为什么 Pipeline ≠ 事务

维度 Pipeline MULTI/EXEC
网络往返 1 次 1 次(EXEC 时)
原子性 ❌ 中间可插入其他客户端命令 ✅
错误处理 逐条看结果 逐条看结果

"Pipeline 更快是因为少 RTT",不是因为原子——这点常被误解。


4. Lua 的正确用法与限制

要点 说明
KEYS[] / ARGV[] 传参规范(不要拼接字符串)
脚本要短 长脚本会阻塞全局(B01 实测 1441 ms)
只做纯计算+少量命令 不要在 Lua 里循环跑几万次
SCRIPT LOAD + EVALSHA 减少传输

实测(原子计数):

redis-cli eval "return redis.call('incrby', KEYS[1], 5)" 1 cnt
# → 5

5. WATCH:乐观锁的替代

WATCH key → 读 → MULTI → 写 → EXEC
EXEC 返回 nil 表示 key 被别人改过 → 重试
特点 说明
乐观 不加锁,冲突才失败
需重试 高冲突下性能差
已被 Lua 取代 Lua 更简单直接

6. 五个误区

误区 真相
"MULTI 能回滚" 实测不回滚
"Lua 出错会撤销" 已执行的命令保留(实测 k_c=1)
"Pipeline 是事务" 只是批量发送
"Lua 越长越好" 长脚本阻塞全局
"WATCH 没有代价" 冲突要重试

7. 自检

  1. 三者各保证什么、不保证什么?
  2. 实测里 MULTI 内 INCR 失败,两条 SET 生效了吗?
  3. Lua 出错后已执行的命令会撤销吗?
  4. Pipeline 为什么快?
  5. 什么时候该用 WATCH?

8. 下一篇

S12-J05 · 高级用法:延迟队列 / 订阅 / 限流 / SCAN

事务讲完,下一篇把几个高频用法落地,并解释为什么生产禁用 KEYS(已有三组实测)。


本篇属于系列《S12 · Redis 与缓存》。旧文来源:A-876/1572/1570;扩展点 E-S12-013。事务与 Lua 错误语义为 Redis 7.0.15 实测。

S12-J04 · 事务、Lua 与 Pipeline
https://www.clxhxhhr.top/posts/5353/
作者
clxstart
发布于
2024-12-31
许可协议
CC BY-NC-SA 4.0
评论
0 条
还没有评论,先写一条吧。