Redis的事务机制
一、什么是事务
事务这个机制我相信大家并不陌生,在日常开发工作中的很多地方都能碰上,像我们平常使用的关系型数据库如MySQL和Oracle都提供了事务机制,这种机制主要用于当我们希望一组动作要么一起成功,要么就一起失败的场景,比如银行卡转账,A给B转了1000元,那么A账户就需要扣除1000元,而B账户相应的就需要增加1000元,这两个动作必须保证要么一起成功,要么就一起失败,否则就会出现不一致的问题,而这个就可以通过事务机制来保证。
二、Redis的事务原理
Redis也提供了上述所说的事务机制,那么Redis的整个事务流程是什么样的呢?我们先来看一下下面这张图:

Redis可以通过MULTI指令来开启事务,开启之后,后面执行的命令都不会立即执行,而是被存入到一个缓存队列中,当我们使用EXEC指令之后,Redis服务端会按照我们提交的顺序逐个执行。
三、如何使用Redis事务
Redis事务机制给我们提供了如下四个指令:
- MULTI:用于开启事务
- EXEC:用于提交事务
- DISCARD:用于取消事务
- WATCH:用于监听指定的Key
下面我们使用Redis的客户端来进行演示:
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379(TX)> set A -1000
QUEUED
127.0.0.1:6379(TX)> set B 1000
QUEUED
127.0.0.1:6379(TX)> EXEC
1) OK
2) OK
上面就是一个事务正常执行的样子,如果我们想取消事务的话,可以使用DISCARD指令,它会清空缓存队列保存的所有命令:
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379(TX)> set A 500
QUEUED
127.0.0.1:6379(TX)> set B -500
QUEUED
127.0.0.1:6379(TX)> DISCARD
OK
下面我们再来看一下WATCH指令,这个指令是用来监听某个Key的,当我们刚好通过事务来修改这个Key的话,如果在提交事务之前有其他的客户端或者Session把这个Key给修改了,那么整个事务都不会被执行,有点类似于CAS。
# 客户端1:修改WATCH_KEY的值为BBB,暂不提交
127.0.0.1:6379> set WATCH_KEY AAA
OK
127.0.0.1:6379> WATCH WATCH_KEY
OK
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379(TX)> set WATCH_KEY BBB
QUEUED
#客户端2:修改WATCH_KEY的值为CCC
127.0.0.1:6379> set WATCH_KEY CCC
OK
#客户端1:提交事务
127.0.0.1:6379(TX)> EXEC
(nil)
127.0.0.1:6379> get WATCH_KEY
"CCC"
可以看到,事务没有执行成功,最终拿到的值还是"CCC"。这里还有一种情况需要注意,如果是在同一个客户端或者Session中,通过WATCH去监听某一个Key之后,再去修改这个Key,然后才开启事务,这个时候事务仍然会执行失败:
127.0.0.1:6379> set WATCH_KEY_2 AAA
OK
127.0.0.1:6379> WATCH WATCH_KEY_2
OK
127.0.0.1:6379> set WATCH_KEY_2 BBB
OK
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379(TX)> SET WATCH_KEY_2 CCC
QUEUED
127.0.0.1:6379(TX)> EXEC
(nil)
127.0.0.1:6379> get WATCH_KEY_2
"BBB"
即便你在事务内部并不涉及到这个Key,也照样失败:
127.0.0.1:6379> SET WATCH_KEY_3 AAA
OK
127.0.0.1:6379> WATCH WATCH_KEY_3
OK
127.0.0.1:6379> set WATCH_KEY_3 BBB
OK
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379(TX)> get WATCH_KEY_1
QUEUED
127.0.0.1:6379(TX)> EXEC
(nil)
127.0.0.1:6379> get WATCH_KEY_3
"BBB"
四、Redis事务回滚
我们知道,凡是支持事务机制的,一般都支持回滚,Redis也同样支持,但是需要注意的是,Redis的回滚是一种“弱回滚”,它只能对一些基本语法错误进行回滚,但是如果是运行时错误,就做不到了,下面我们通过示例来看一下:
# 基本语法错误可以回滚
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379(TX)> set hg_key1 111
QUEUED
127.0.0.1:6379(TX)> sett hg_key2 222
(error) ERR unknown command `sett`, with args beginning with: `hg_key2`, `222`,
127.0.0.1:6379(TX)> EXEC
(error) EXECABORT Transaction discarded because of previous errors.
127.0.0.1:6379> get hg_key1
(nil)
#运行时错误无法回滚
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379(TX)> set hg_key3 333
QUEUED
127.0.0.1:6379(TX)> sadd hg_key3 333
QUEUED
127.0.0.1:6379(TX)> EXEC
1) OK
2) (error) WRONGTYPE Operation against a key holding the wrong kind of value
127.0.0.1:6379> get hg_key3
"333"
可以看到,对于简单的语法错误,是可以回滚整个事务的,但是碰到第二种情况时,没有语法上的错误,导致第一条命令成功执行了。

浙公网安备 33010602011771号