回答
首先,Redis 是支持事务的。Redis 的事务机制提供了一种一次性、按顺序执行一组命令的机制。
它的实现原理是将一组命令压入到事务队列中,然后按照 FIFO 的顺序依次执行,同时在执行这一组命令期间不允许其他命令插入,保证了这组命令在一个原子操作中完成。
Redis 提供以下 5 个命令来实现事务:
MULTI
:用来标记一个事务块的开始。EXEC
:执行事务块中的所有命令。DISCARD
:取消事务,放弃执行事务块内的所有命令。这个命令会清除事务队列并退出事务。WATCH
:用于乐观锁,它可以监视一个或多个键,如果在执行事务前这些键被其他命令改变了,那么事务将被取消(EXEC
会返回null表示事务未执行)UNWATCH
:取消 WATCH 命令对所有 key 的监视。
Redis 事务详解
在 Redis 中,一个事务从开始到结束会经历以下3个阶段:
- 事务开始
- 命令入队
- 事务执行
由 MULTI
命令开启事务,EXEC
命令执行事务,其形式如下:
multi
command1()
command2()
...
commandn()
exec/discard
multi
标志着事务的开始,在 multi
之后的命令都不会执行,全部进入事务队列中,直到服务器接收 exec 或者 discard 才会开始执行或者放弃整个事务,当执行完整个事务后,Redis 会一次性返回所有命令的运行结果。如下:
事务开启:MULTI
MULTI
标志着事务的开始,可以将执行该命令的客户端从非事务态切换的事务态。
从 MULTI
命令后,Redis 接受该客户端的后续命令后并不会立刻执行,而是将其放置在事务队列中。
该命令的运行如下:
127.0.0.1:6379> MULTI
OK
总是会返回 OK 字符串。
命令入队
当客户端处于非事务态时,这个客户端发送的命令会被立刻执行:
当客户端使用 MULTI
命令进入事务态,这个时候客户端发送的命令不会立刻执行,而是将其放入到事务队列中,向客户端回复 QUEUED
,如下:
事务执行:EXEC
当我们在一个处于事务态的客户端执行EXEC
,服务器会按照 FIFO 的顺序执行该客户端对应的事务队列中所有命令,然后将执行结果一次性返回给客户端:
使用 EXEC
执行后,客户端会恢复到非事务状态。
事务取消:DISCARD
DISCARD
清除所有先前在一个事务中放入的队列的命令,然后恢复到非事务状态。
监视器:WATCH
WATCH
用于在执行事务时监视一个或多个 key。如果在事务执行之前,这些键的值被其他命令改变了,那么这个事务将会被拒绝执行,并向客户端返回 nil。
即,在一个事务开始之前,我们可以通过 WATCH
命令监视一个或多个 key。一旦某个 key 被WATCH
,如果该 key 在我们执行 EXEC
命令之前被修改(包括设置新值、删除等),Redis 将取消这个事务的执行。例如:
我们用 WATCH
监视 key balance,然后开启事务,执行 DECRBY balance 50
,注意这个时候不要立刻执行 EXEC
,我们需要另开一个客户端,将 balance 改变:
然后再执行 EXEC
:
这个时候客户端会返回 nil,表明拒绝了该事务的执行。
最后
一般不推荐在实际工作中使用 Redis 事务,原因是 Redis 事务有诸多坑: