在数据库领域,“事务”(Transaction)是一个可靠的词,它通常与ACID(原子性、一致性、隔离性、持久性)四大特性紧密绑定,是保证数据绝对可靠的基石。然而,当我们谈论Redis的事务时,会发现它是一个非常“另类”的存在。
Redis的事务同样允许我们将一组命令打包执行,但它所做的承诺,与我们熟知的关系型数据库事务大相径庭。
是什么:Redis事务的本质
Redis事务的本质,是一个命令的集合。它允许客户端将多个命令打包,然后一次性、按顺序地执行,期间不会被其他客户端的命令打扰。
简单来说,Redis事务的核心就是 “一组命令,序列化,不许加塞” 。
它主要围绕四个核心命令来工作:
-
MULTI:开启一个事务块。此命令之后的所有命令都会被放入一个队列中,而不会立即执行。 -
EXEC:执行事务队列中的所有命令。 -
DISCARD:放弃事务。清空事务队列,并退出事务状态。 -
WATCH:一个乐观锁,用于在事务执行前,监视一个或多个key。如果在EXEC执行前,被监视的key被其他客户端修改了,那么整个事务将被取消。
能干嘛:Redis事务的核心保证
Redis事务提供了一个在队列中一次性、顺序性、排他性地执行一系列命令的能力。
想象一下去银行办理业务,你需要在柜台连续办理“查询余额”、“取款”、“转账”三个操作。
- 没有事务:你每办完一个业务,就可能被插队,需要重新排队,这三个操作可能会被其他人的业务打断。
- 使用Redis事务:你相当于告诉柜员:“接下来我要办三个业务,请不要接待其他人,直到我全部办完。” 柜员会把你的三个请求单(命令)收好,然后关上窗口,一件一件为你办完,再打开窗口接待下一个人。
怎么玩:Redis事务三部曲
一个典型的Redis事务流程分为三个阶段:
开启事务 (
MULTI )- 客户端发送
MULTI命令。 - 服务器返回
OK,并进入事务状态。 - 此后,客户端发送的所有命令(如
SET,INCR等)都会被服务器接收并放入一个先进先出的事务队列中,服务器会返回QUEUED,表示命令已入队,但并未执行。
- 客户端发送
命令入队
- 客户端继续发送多个命令。
- 服务器逐一将这些命令存入事务队列。
执行或放弃事务 (
EXEC / DISCARD )- 执行 (
EXEC ) :客户端发送EXEC命令。Redis会原子地、顺序地执行事务队列中的所有命令。所有命令执行完毕后,会将所有结果一次性返回给客户端。 - 放弃 (
DISCARD ) :如果客户端在EXEC之前发送DISCARD命令,服务器会清空整个事务队列,然后退出事务状态,什么都不会发生。
- 执行 (
示例:
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> SET balance 1000
QUEUED
127.0.0.1:6379> DECRBY balance 200
QUEUED
127.0.0.1:6379> INCRBY transfer_count 1
QUEUED
127.0.0.1:6379> EXEC
1) OK
2) (integer) 800
3) (integer) 1Redis事务 vs. 数据库事务:截然不同的哲学
这是理解Redis事务最关键的部分。我们不能用看待MySQL事务的眼光去看待Redis事务。

不保证原子性 (Atomicity)
- 数据库事务:严格保证原子性。事务中的所有操作,要么全部成功,要么全部失败回滚。
- Redis事务:不保证原子性,不支持回滚。如果一个事务队列中的某个命令在执行时出错(例如,对一个字符串类型的key执行列表操作),只有这个出错的命令会失败,而其他正确的命令会继续执行。
- 为什么这么设计? Redis的作者认为,命令执行失败通常是编程错误(如语法错误)导致的,这类问题应该在开发阶段就被发现,而不是通过运行时的回滚机制来解决。Redis追求的是极致的简单和高性能,回滚机制会使内部逻辑复杂化。
独特的隔离性 (Isolation)
数据库事务:有多种隔离级别(读未提交、读已提交、可重复读、串行化)来处理复杂的并发读写问题。
Redis事务:没有隔离级别的概念。但它通过另一种方式保证了隔离:
- 排他性执行:由于Redis是单线程执行命令的,当一个事务通过
EXEC开始执行时,它会独占整个线程,直到队列中的所有命令都执行完毕。在此期间,任何其他客户端的命令都无法插入。 - 执行前不可见:在
EXEC执行之前,所有入队的命令都只是存在队列里,没有被实际执行。因此,不存在“事务内的查询要看到事务里的更新,而事务外的查询不能看到”这种复杂的隔离问题。
- 排他性执行:由于Redis是单线程执行命令的,当一个事务通过
小结
Redis事务的设计,是一种典型的权衡与取舍,它舍弃了关系型数据库那种重量级的、绝对可靠的事务模型,换取了极致的简单和高性能。
它保证了什么?
- 批量操作的原子性:从发送
EXEC到执行结束,这一整个“执行批量命令”的动作是原子的,不会被中断。 - 顺序性:所有命令会严格按照入队的顺序执行。
- 排他性:事务执行期间,不会有其他命令来“插队”。
- 批量操作的原子性:从发送
它不保证什么?
- 命令级别的原子性:不保证队列中的所有命令都同时成功或失败。
- 回滚能力:没有事务执行到一半进行回滚的能力。
因此,在使用Redis事务时,我们应将其理解为一个 “批量命令执行器” ,它能确保一组操作被连续、不间-断地执行。这在很多场景下已经足够,例如需要一次性更新多个相关key,而又不希望被其他操作打断的场景。对于需要强原子性保证的业务,则可能需要通过Lua脚本或在客户端层面实现更复杂的逻辑。