Redis事务

事务的本质:一组命令的集合。一个事务中的所有的命令都会被序列化,在事务执行的过程中,会按照顺序执行。

Redis 事务可以一次执行多个命令, 并且带有以下三个重要的保证:

  1. 批量操作在发送 EXEC 命令前被放入队列缓存。

  2. 收到 EXEC 命令后进入事务执行,事务中任意命令执行失败,其余的命令依然被执行

  3. 在事务执行过程,其他客户端提交的命令请求不会插入到事务执行命令序列中。

一个事务从开始到执行会经历以下三个阶段:

  1. 开始事务(multi)。

  2. 命令入队(……)。

  3. 执行事务(exec)。

注意:

  • redis中的单挑命令保证原子性,但是redis事务不保证原子性。

  • redis中没有隔离级别的概念

  • 所有的事务在事务中并没有直接被执行,只有发起执行命令(Exec)的时候才会执行。

下表列出了 redis 事务的相关命令:

序号 命令及描述
1 DISCARD 取消事务,放弃执行事务块内的所有命令。
2 EXEC 执行所有事务块内的命令。
3 MULTI 标记一个事务块的开始。
4 UNWATCH 取消 WATCH 命令对所有 key 的监视。
5 [WATCH key key …] 监视一个(或多个) key ,如果在事务执行之前这个(或这些) key 被其他命令所改动,那么事务将被打断。

正常执行事务:

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> mset k3 v3 k4 v4
QUEUED
127.0.0.1:6379> get k1
QUEUED
127.0.0.1:6379> get k2
QUEUED
127.0.0.1:6379> mget k3 k4
QUEUED
#————————————————————————————————
127.0.0.1:6379> exec #执行事务
1) OK
2) OK
3) OK
4) "v1"
5) "v2"
6) 1) "v3"
   2) "v4"
 
 #可以发现事务中的命令会按照入队的顺序执行(顺序性)

取消事务

127.0.0.1:6379> multi #开启事务
OK
#-----------命令入队---------------
127.0.0.1:6379> set a 1
QUEUED
127.0.0.1:6379> set b 2
QUEUED
127.0.0.1:6379> set c 3
QUEUED
#————————————————————————————————
127.0.0.1:6379> discard  #取消事务
OK
127.0.0.1:6379> get a #得到的a值为nil,所以事务没有执行
(nil)
127.0.0.1:6379> 

编译时异常(代码有问题,命令写错),整个事务就不会执行

127.0.0.1:6379> multi #开启事务
OK
#-----------命令入队---------------
127.0.0.1:6379> set k1 v1
QUEUED
127.0.0.1:6379> sett k2 v2 #命令写错
(error) ERR unknown command `sett`, with args beginning with: `k2`, `v2`, 
127.0.0.1:6379> set k3 v3
QUEUED
#————————————————————————————————
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> 

运行时异常(代码正确但是命令执行失败),其他命令会正常执行,执行失败的命令会抛出异常

127.0.0.1:6379> set a "a"
OK
127.0.0.1:6379> incr a
(error) ERR value is not an integer or out of range #字符串无法加一,报错

127.0.0.1:6379> multi #开启事务
OK
#-----------命令入队---------------
127.0.0.1:6379> incr a
QUEUED
127.0.0.1:6379> set k1 v1
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> get k1
QUEUED
#————————————————————————————————
127.0.0.1:6379> exec #执行事务
1) (error) ERR value is not an integer or out of range #第一条命令报错执行失败,后续命令继续执行
2) OK
3) OK
4) "v1"
127.0.0.1:6379> 

watch 监视(实现乐观锁和悲观锁)

  • watch命令可以被调用多次。对键的监视从watch执行之后开始生效,直到调用exec为止,不管事务是否成功执行,对所有键的监视都会被取消。

  • 当客户端断开连接时,该客户端对键的监视也会被取消。

  • unwatch命令可以手动取消对所有键的监视。

1.监视money,正常执行

127.0.0.1:6379> set money 100
OK
127.0.0.1:6379> set outmoney 0
OK
127.0.0.1:6379> watch money #监视money
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> decrby money 20
QUEUED
127.0.0.1:6379> incrby outmoney 20
QUEUED
127.0.0.1:6379> exec #事务执行前,money未被其他命令修改
1) (integer) 80
2) (integer) 20

2.监视money,事务提交前money被其他命令修改

客户端1:

127.0.0.1:6379> set money  500 #如果在客户端2事务执行前,执行这条命令
OK

客户端2:

127.0.0.1:6379> mget money outmoney
1) "70"
2) "30"
127.0.0.1:6379> watch money
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> decrby money 10
QUEUED
127.0.0.1:6379> incrby outmoney 10
QUEUED
127.0.0.1:6379> exec #事务执行前,money被其他命令修改,所以事务被打断
(nil)
127.0.0.1:6379>