Skip to content

Redis事务

Redis事务并非传统关系型数据库(如MySQL)中带复杂隔离级别和回滚机制的事务。它的本质是一组命令的集合,这组命令会被打包、序列化,然后按顺序、排他性地执行,执行期间不会被其他客户端的命令插入。

核心特征: * 一次性 (一次性提交) * 顺序性 (按入队顺序执行) * 排他性 (执行时不被中断)


一、 事务相关命令及使用

Redis事务主要通过以下四个命令实现:

  1. MULTI开启事务。此命令之后的所有命令都不会立即执行,而是被放入一个事务队列中。
  2. EXEC执行事务。原子性地执行队列中的所有命令。
  3. DISCARD取消事务。清空事务队列,放弃执行。
  4. WATCH key [key ...]监视一个或多个键。如果在MULTI执行后、EXEC执行前,被监视的键被其他客户端修改,则整个事务将被取消。

使用场景示例

  • 标准事务MULTI SET key1 val1 SET key2 val2 EXEC

  • 错误处理:Redis事务对错误的处理方式比较特殊。

    • 语法错误(入队时错误):如果一个命令在入队时就发现语法错误(如sets而非set),整个事务会被标记为失败。执行EXEC时,所有命令都不会被执行。
    • 运行时错误(执行时错误):如果命令语法正确,但执行时因类型不匹配等原因出错(如对String类型执行LPUSH),Redis不会回滚。它会跳过这条错误的命令,继续执行后续的正确命令。这是Redis事务与传统数据库事务一个核心区别。
  • 使用WATCH实现乐观锁(CAS)WATCH是实现Check-And-Set (CAS)行为的关键,常用于解决并发场景下的竞争条件。

    • 场景:原子性地给一个key的值加1(不使用INCR命令)。
    • 流程
      1. WATCH mykey
      2. val = GET mykey (客户端获取当前值)
      3. val = val + 1 (客户端计算新值)
      4. MULTI
      5. SET mykey $val
      6. EXEC
    • 工作原理:如果在第1步和第6步之间,有其他客户端修改了mykey,那么第6步的EXEC会失败(返回nil),事务被取消。应用程序需要捕获这个失败,然后重试整个操作,直到成功为止。这种机制被称为乐观锁
    • UNWATCH:可以取消对所有键的监视。

二、 Redis事务的执行步骤

Redis事务的执行分为三个明确的阶段:

  1. 开启 (MULTI):客户端进入事务状态。
  2. 入队 (Queuing):客户端发送的命令(EXEC/DISCARD/WATCH/MULTI除外)被放入一个先进先出的事务队列,服务器返回QUEUED
  3. 执行 (EXEC):服务器按顺序执行队列中的所有命令,并将结果一次性返回给客户端。

三、 深入理解Redis事务

1. 为什么Redis不支持回滚?

Redis官方给出的理由是:

  • 命令失败通常是编程错误:Redis命令失败的主要原因是语法错误或对错误类型的数据进行操作。这些问题应该在开发阶段被发现和修复,而不是依赖生产环境的回滚机制。
  • 保持简单和高性能:不支持回滚使得Redis的内部实现可以更简单、更快速。
  • 回滚无法解决逻辑错误:即使有回滚,也无法处理业务逻辑上的错误(例如,本该加1却写成了加2)。

2. Redis事务的ACID性质分析

  • A - 原子性 (Atomicity)

    • Redis部分满足原子性。从其定义“要么全部执行,要么全部不执行”来看,它是原子的。但由于它在遇到运行时错误时不回滚,而是继续执行,这与传统数据库的“完全成功或完全回滚”的原子性定义有所不同。
  • C - 一致性 (Consistency)

    • 满足。事务执行前后,数据库从一个一致性状态转移到另一个一致性状态。如果发生错误(如语法错误导致事务取消),数据会恢复到事务执行前的状态。即使是运行时错误,也只是跳过错误命令,不会破坏数据的一致性。
  • I - 隔离性 (Isolation)

    • 满足。由于Redis是单线程模型(6.0前),一个事务在执行EXEC时,会排他性地执行队列中的所有命令,期间不会被其他客户端的命令打断,保证了完美的隔离性。
  • D - 持久性 (Durability)

    • 不满足。Redis事务的持久性依赖于其持久化配置(RDB/AOF)。由于持久化通常是异步执行的,EXEC执行成功后,数据不一定已经写入磁盘。如果此时服务器宕机,事务的修改可能会丢失。

四、 其他实现事务的方式

  • Lua脚本:Redis保证Lua脚本的原子性执行。可以将一组命令放在一个Lua脚本中,效果类似于事务,同样不支持运行时错误的回滚。
  • 基于标记变量:通过一个额外的key作为“锁”或“状态标记”来手动控制逻辑上的事务,这种方式实现复杂,需要应用层编写大量代码。

总结表

特性 Redis事务的实现与特点
核心机制 MULTI开启,命令入队,EXEC原子执行。
原子性 部分满足。保证命令被一次性、顺序性、排他性地执行,但运行时错误不回滚
隔离性 满足。单线程模型天然保证了事务执行期间的隔离。
持久性 不满足。依赖于异步的RDB/AOF持久化策略。
并发控制 通过WATCH命令实现乐观锁 (CAS),在数据被修改时取消事务,需要客户端重试。
适用场景 需要将多个命令打包执行,以减少网络往返和保证操作序列不被打断的场景,但不适用于需要严格回滚的金融等业务。