学了这么久的Redis,竟然还不知道事务

244 阅读2分钟

Redis支持事务吗,有说支持的,有说半支持的,我这里进行一波演示,看看到底支持不支持。在演示事务之前,首先要了解一下这几个命令。
multi开启事务
exec执行事务
discard取消事务
watch key [key ...]监视事务
unwatch取消监视事务
下面通过一个转账的例子进行演示事务。

127.0.0.1:6379> multi
OK
127.0.0.1:6379> set money 100
QUEUED
127.0.0.1:6379> get money
QUEUED
127.0.0.1:6379> exec
1) OK
2) "100"

这是一个正常的事务问题,就是开启了一个事务,事务里有一组数据,然后执行,这里的每一条指令都是放到一个队列里的,当执行的时候,会逐条执行。
下面演示一个取消事务的

127.0.0.1:6379> multi
OK
127.0.0.1:6379> set money 100
QUEUED
127.0.0.1:6379> get money
QUEUED
127.0.0.1:6379> discard
OK

这个也是正常的取消了
如果在我们执行事务的时候,出现了语法错误,那么整个事务都不会被执行了

127.0.0.1:6379> multi
OK
127.0.0.1:6379> set money 100
QUEUED
127.0.0.1:6379> sett age 20
(error) ERR unknown command `sett`, with args beginning with: `age`, `20`, 
127.0.0.1:6379> get money
QUEUED
127.0.0.1:6379> exec
(error) EXECABORT Transaction discarded because of previous errors.

可以看出,当出现语法错误的时候,整个事务都不会被执行,还有一种错误,不是语法错误,而是运行时错误。

127.0.0.1:6379> multi
OK
127.0.0.1:6379> set name "hzy"
QUEUED
127.0.0.1:6379> incr name
QUEUED
127.0.0.1:6379> get name
QUEUED
127.0.0.1:6379> exec
1) OK
2) (error) ERR value is not an integer or out of range
3) "hzy"

我们让一个字符串自增,出现了错误,但是事务的其他语句还是正常执行了(这应该就是别人说的Redis是半支持事务吧)。
还有一种情况就是,当我们执行事务的时候,还未执行exec,然后有其他客户端来干扰了。

127.0.0.1:6379> multi
OK
127.0.0.1:6379> set money 100
QUEUED
127.0.0.1:6379> incr money
QUEUED
127.0.0.1:6379> exec
1) OK
2) (integer) 101

如果在执行exec之前,另一个客户端执行了如下操作

127.0.0.1:6379> set money 500
OK

然后客户端1再执行exec,我们get一下

127.0.0.1:6379> get money
"101"

可以看出,这是事务执行完的结果,而那个500就没有成功,为了解决这一问题,出现了锁,用watch监视,也就是我们的乐观锁。

127.0.0.1:6379> watch money
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set money 100
QUEUED
127.0.0.1:6379> incr money
QUEUED
127.0.0.1:6379> exec
(nil)
127.0.0.1:6379> get money
"500"

这里我们对money进行了监视,在执行exec之前我们在客户端2执行如下

127.0.0.1:6379> set money 500
OK

然后再执行exec,由于对money进行了watch监视,所以就返回了nil。当我们再get money的时候,此时就是刚刚设置的500了,而事务相当于没有执行。
这里需要注意,当我们执行了exec、discard或unwatch的时候,都会自动的取消监视。