Redis事务是一组命令的集合,它将这一组命令序列化到队列中顺序执行。

Redis事务是一种弱事务,因为在Redis命令执行失败的时候,

  • 并不会导致前面的命令回滚,
  • 也不会阻止后面命令的执行。

总结就是在Redis执行之前命令集如果有问题,它会执行假回滚,其实就是不执行命令集(因为没有执行哪里来的回滚),如果运行时有错误,错了就是错了。

redis事务是通过四个命令组成,它们分别是:

  • MULTI:开启事务
  • EXEC: 执行事务
  • DISCARD: 取消事务
  • WATCH:监视

在面试中我们只需要记住Redis事务没有回滚,Redis可以使用watch监视命令执行回滚即可,下面通过一些例子来说明。

1)正常执行事务的例子

127.0.0.1:6379> multi
OK
127.0.0.1:6379> set k1 v1
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> exec
1) OK
2) OK
127.0.0.1:6379> get k1
"v1"
127.0.0.1:6379> get k2
"v2"

Redis事务

从截图中我们可以看出,Redis将这一组命令序列化到队列中,顺序执行

2)取消事务例子,命令不执行的例子,这里不是事务的回滚。

127.0.0.1:6379> multi
OK
127.0.0.1:6379> set k1 v1
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> discard
OK
127.0.0.1:6379> exec
(error) ERR EXEC without MULTI
127.0.0.1:6379> get k1
(nil)
127.0.0.1:6379> get k2
(nil)
127.0.0.1:6379>

Redis事务

事务队列命令集中,Redis发现有discard命令,是不会执行命令的。

3)语法错误,命令不执行,这里也不是事务的回滚。

127.0.0.1:6379> multi
OK
127.0.0.1:6379> set k1 v1
QUEUED
127.0.0.1:6379> se t k2 v2
(error) ERR unknown command 'se'
127.0.0.1:6379> exec
(error) EXECABORT Transaction discarded because of previous errors.
127.0.0.1:6379> get k1
(nil)
127.0.0.1:6379> get k2
(nil)

Redis事务

在Redis事务中,将这一组命令

multi

set k1 v1

se t k2 v2

exec

放到队列中,还没有执行,但是此时发现队列中有语法错误,而这个语法错误是可以在Redis执行命令之前发现的。所以有语法错误的时候Redis不会执行命令集中的任何命令。

4) 运行时错误,不可回滚的例子。

127.0.0.1:6379> set k1 v1
OK
127.0.0.1:6379> set k2 v2
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set k1 v111
QUEUED
127.0.0.1:6379> hset k2 name 'yxjc123'
QUEUED
127.0.0.1:6379> exec
1) OK
2) (error) WRONGTYPE Operation against a key holding the wrong kind of value
127.0.0.1:6379> get k1
"v111"
127.0.0.1:6379> get k2
"v2"
Redis事务

因为在本例中,事务对列中,没有语法错误,也没有取消事务的命令,只有运行时错误,所以k1虽然在事务内,但是不回滚。

5)Redis CAS锁

那么如何才能做到Redis事务真正的回滚呢?可以使用watch监视命令,监视某个key的变化,当发现某个key发生了变化则执行回滚。

使用watch的这个操作属于乐观锁,没有用实质的锁,只是在修改的时候和之前的值进行比较,如果和之前相同的值相同则提交,不同则放弃。

例子

打开第一个客户端执行如下操作

127.0.0.1:6379> set k1 v1
OK
127.0.0.1:6379> set k2 v2
OK
127.0.0.1:6379> watch k1
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set k1 v111
QUEUED
127.0.0.1:6379> set k2 v222
QUEUED
127.0.0.1:6379> exec
(nil)
127.0.0.1:6379> get k1
"vvv"
127.0.0.1:6379> get k2
"v2"

打开第二个客户端在事务提交前修改k1的值

127.0.0.1:6379> set k1 vvv
OK

效果如图所示:

Redis事务概念

watch命令监视了k1,在事务命令集中,k1有变化,所以回滚。