Skip to content

Redis事务介绍

1. Redis 事务是什么

Redis 事务不是传统关系型数据库里那种带完整 ACID 语义的事务。

更准确地说,Redis 事务是一种:

  • 把多条命令打包起来
  • 按顺序执行
  • 执行期间不被其他客户端命令插入

的机制。

Redis 事务最核心的命令有 3 个:

  • MULTI
  • EXEC
  • DISCARD

另外还有一个非常关键的配套命令:

  • WATCH

2. Redis 事务的基本流程

2.1 MULTI:开启事务

客户端发送 MULTI 后,Redis 会进入事务上下文。

此时后续命令不会立刻执行,而是:

  • 先进入队列
  • 返回 QUEUED

例如:

MULTI
SET user:1:name alice
INCR counter:order
EXEC

EXEC 之前:

  • SET
  • INCR

都只是入队,不会真正落地执行。

2.2 EXEC:提交事务

执行 EXEC 后,Redis 会把事务队列中的命令:

  • 按顺序执行
  • 一次性执行完

并返回每条命令的结果数组。

2.3 DISCARD:取消事务

如果在 EXEC 前决定不提交,可以执行 DISCARD

  • 清空事务队列
  • 退出事务状态

3. Redis 事务到底保证了什么

3.1 保证“批量顺序执行”

Redis 事务最重要的保证是:

  • 事务中的命令会按入队顺序执行

不会出现:

  • 你的第 2 条命令先于第 1 条执行

3.2 保证“执行阶段不被其他命令打断”

Redis 单线程事件循环下,同一时刻核心命令执行路径是串行的。

所以 EXEC 开始后:

  • 事务里的命令会连续执行
  • 其他客户端命令不会插进中间

这一点是 Redis 事务的“原子性来源”。

但要注意:

  • Redis 保证的是事务整体执行期间不被打断
  • 不是说“事务中的所有业务逻辑都具备数据库级回滚能力”

3.3 不保证传统数据库意义上的回滚

这是 Redis 事务和 MySQL 事务最大的不同之一。

例如事务里有三条命令:

  1. SET a 1
  2. INCR b
  3. 某条命令执行时报错

Redis 不会像 MySQL 那样:

  • 自动把前面已经执行成功的命令回滚掉

也就是说:

  • Redis 事务没有传统意义上的 rollback

4. Redis 事务和关系型数据库事务有什么区别

4.1 Redis 不追求完整 ACID

关系型数据库事务强调:

  • 原子性(Atomicity)
  • 一致性(Consistency)
  • 隔离性(Isolation)
  • 持久性(Durability)

而 Redis 事务更像是:

  • 命令批处理 + 顺序执行 + 简单乐观锁

它和 MySQL 事务的设计目标根本不同。

4.2 Redis 没有“执行失败自动回滚”

MySQL 中:

  • 一个事务里后续语句失败
  • 可以选择整体回滚

Redis 中:

  • EXEC 一旦开始
  • 已成功执行的命令不会自动撤销

4.3 Redis 的隔离性也不是 MVCC / 锁事务那套

Redis 的“隔离”主要来自:

  • 单线程串行执行
  • 事务执行阶段不被打断

而不是:

  • 行锁
  • MVCC
  • 两阶段锁

所以不要把 Redis 事务直接套用 MySQL 的事务模型去理解。

4.4 一张表看差异

维度 Redis 事务 MySQL 事务
核心机制 命令入队 + 顺序执行 日志 + 锁/MVCC + 提交回滚
执行期间是否被打断 不会 取决于锁与隔离级别
回滚 不支持传统自动回滚 支持
隔离实现 单线程串行执行 锁 / MVCC
乐观并发控制 WATCH 版本号、条件更新等
适用场景 简单原子批处理 强一致事务业务

5. Redis 事务中的两类错误

这是面试里很容易问细的点。

5.1 入队阶段错误

如果在 MULTI 后,某条命令在入队阶段就有问题,例如:

  • 命令名拼错
  • 参数个数不对

那么这类错误会导致:

  • 整个事务在 EXEC 时被拒绝执行

也就是说,这种错误属于“事务还没真正开始执行就已经非法”。

5.2 执行阶段错误

另一类错误是命令能成功入队,但执行时失败。

例如:

SET counter hello
MULTI
INCR counter
SET user:1:name alice
EXEC

这里:

  • INCR counter 会因为值不是整数而报错

但 Redis 的行为是:

  • 这条命令报错
  • 后面的命令仍然继续执行
  • 不会自动回滚前面或后面的成功命令

所以 Redis 事务的错误处理一定要记住:

  • 不是全有或全无

6. WATCH:Redis 的乐观锁

6.1 WATCH 是做什么的

WATCH 用来监视一个或多个 key。

如果在事务提交前,这些 key 被其他客户端修改了,那么:

  • 当前事务的 EXEC 会失败
  • Redis 返回 nil / 空结果
  • 表示这次事务没有提交成功

它的本质是:

  • 乐观锁(Optimistic Locking)

6.2 为什么需要 WATCH

因为单靠 MULTI / EXEC 只能保证:

  • 一批命令顺序执行

但无法保证:

  • 你“读到的数据”和“提交更新时的数据”之间没有被别人改过

这就是典型的“读-改-写竞争”问题。

例如你想实现:

  1. 先读库存
  2. 判断库存是否足够
  3. 再扣减库存

如果没有并发控制,就可能出现多个客户端同时读到旧值,然后都去扣减。

6.3 WATCH 的工作方式

典型流程如下:

WATCH stock:1001
GET stock:1001
MULTI
DECR stock:1001
EXEC

如果在:

  • WATCH stock:1001
  • EXEC

之间,其他客户端修改了 stock:1001,那么当前 EXEC 会失败。

这意味着:

  • Redis 没帮你自动重试
  • 你需要在客户端重试整套逻辑

6.4 UNWATCH 是做什么的

UNWATCH 用来取消当前连接上对所有 key 的监视。

另外:

  • 执行 EXEC
  • 执行 DISCARD

后通常也会自动取消监视状态。

7. Redis 事务的典型使用场景

7.1 多条命令需要连续执行

例如:

  • 先写业务数据
  • 再更新计数器
  • 再记录日志索引

如果你希望:

  • 这些命令不要被其他客户端插进来

那么可以使用事务。

7.2 基于 WATCH 的 CAS 更新

比如:

  • 扣库存
  • 扣余额
  • 更新某个版本号

这类“先读后改”的逻辑,可以用 WATCH 做乐观控制。

7.3 简单原子批处理

有些场景不是强事务,只是想减少中间态暴露:

  • 批量写多个 key
  • 多步更新一个对象的多个字段

这时 MULTI / EXEC 可以让这批命令作为一个连续片段执行。

8. Redis 事务的局限性

8.1 不能替代数据库事务

如果你的业务要求:

  • 强一致
  • 回滚
  • 隔离级别
  • 跨多资源复杂事务

Redis 事务通常不够。

8.2 不能表达复杂业务逻辑

因为 Redis 事务里:

  • 命令先入队
  • 再一次性执行

所以你很难在事务内部写出复杂条件分支。

很多“读-判断-写”的逻辑如果拆成多次客户端交互,会暴露并发窗口。

8.3 WATCH 冲突时要自己重试

WATCH 失败后:

  • Redis 不会自动帮你重试
  • 需要客户端自己重新读、重新判断、重新提交

在高冲突场景下,重试成本可能比较高。

8.4 长事务队列没有关系型事务那种收益

Redis 事务不适合把大量命令长时间堆在一个事务里。

原因包括:

  • 可读性差
  • 一旦失败不好恢复
  • 执行阶段会连续占用 Redis 处理时间
  • 影响其他请求延迟

9. Lua 脚本和 Redis 事务是什么关系

9.1 为什么很多场景更推荐 Lua

在 Redis 里,很多人做原子操作时,第一反应不是事务,而是 Lua 脚本。

原因是 Lua 更适合表达:

  • 读取
  • 判断
  • 写入

这一整段逻辑。

例如:

  • 如果库存大于 0 就扣减,否则返回失败

用事务写会变成:

  • GET
  • 客户端判断
  • WATCH
  • MULTI
  • DECR
  • EXEC

而 Lua 可以把整个逻辑放在服务端一次完成。

9.2 Lua 的优势

Lua 脚本在 Redis 中执行时,同样具备:

  • 原子执行
  • 执行期间不被其他命令打断

但它比事务更强的地方在于:

  • 可以在服务端写条件逻辑
  • 可以减少客户端与 Redis 的往返次数
  • 更适合“读-判断-写”型原子操作

9.3 什么时候用事务,什么时候用 Lua

可以简单这样区分:

  • 事务
  • 适合“多条命令打包顺序执行”
  • 逻辑较简单

  • Lua

  • 适合“读 + 判断 + 更新”一体化
  • 需要服务端原子逻辑

10. Redis 事务和 Pipeline 的区别

这两个概念也容易混淆。

10.1 Pipeline 是什么

Pipeline 的目标是:

  • 减少网络往返次数

它会把多条命令批量发送给 Redis,但:

  • 不保证事务语义
  • 不保证执行期间不被其他客户端命令插入

10.2 事务和 Pipeline 的核心区别

  • 事务
  • 强调顺序执行和执行阶段不被打断

  • Pipeline

  • 强调减少 RTT、提升吞吐

一句话:

  • Pipeline 解决的是网络开销
  • 事务解决的是一组命令的原子连续执行