Redis(十一)——Redis 的基本事务操作

436 阅读4分钟

“这是我参与8月更文挑战的第14天,活动详情查看:8月更文挑战

1、事务概述

事务的本质

单个 Redis 命令的执行是原子性的,但 Redis 没有在事务上增加任何维持原子性的机制,所以 Redis 事务的执行并不是原子性的。

事务可以理解为一个打包的批量执行脚本,但批量指令并非原子化的操作,中间某条指令的失败不会导致前面已做指令的回滚,也不会造成后续的指令不做。

总结说:redis事务就是一次性、顺序性、排他性的执行一个队列中的一系列命令。

事务执行过程

Redis提供的事务是将多个命令打包,然后一次性、按照先进先出的顺序(FIFO)有序的执行。在执行过程中不会被打断(在事务执行过程,其他客户端提交的命令请求不会插入到事务执行命令序列中),当事务队列中的所以命令都被执行(无论成功还是失败)完毕之后,事务才会结束。

先以 MULTI 开始一个事务, 然后将多个命令入队到事务中, 最后由 EXEC 命令触发事务, 一并执行事务中的所有命令:

  1. 开启事务(MULTI)
  2. 命令入队
  3. 执行事务(EXEC)

redis 事务命令

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

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

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> get k1
 QUEUED
 127.0.0.1:6379> get k2
 QUEUED
 127.0.0.1:6379> set k3 v3
 QUEUED
 127.0.0.1:6379> EXEC  #执行事务,再想执行事务需要重新开启
 1) OK
 2) OK
 3) "v1"
 4) "v2"
 5) OK

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

放弃执行事务

DISCARD命令用于取消事务,放弃执行事务块内的所有命令。

 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

3、事务异常

编译型异常

编译型异常(语法错误),编译不能通过,事务中所有的命令都不会执行。

 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> getset k3 v33
 QUEUED
 127.0.0.1:6379> getset k2   #错误的命令语法
 (error) ERR wrong number of arguments for 'getset' command
 127.0.0.1:6379> set k4 v4
 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> MULTI
 OK
 127.0.0.1:6379> set k1 v1
 QUEUED
 127.0.0.1:6379> incr k1  #命令逻辑错误
 QUEUED
 127.0.0.1:6379> set k2 v2
 QUEUED
 127.0.0.1:6379> set k3 v3
 QUEUED
 127.0.0.1:6379> EXEC  #执行事务
 1) OK
 2) (error) ERR value is not an integer or out of range #运行时异常事务中的逻辑错误命令不会被执行,其他命令正常执行
 3) OK
 4) OK
 127.0.0.1:6379> mget k1 k2 k3
 1) "v1"
 2) "v2"
 3) "v3"

4、监视事务

Redis Watch 命令用于监视一个(或多个) key ,如果在事务执行之前这个(或这些) key 被其他命令所改动,那么事务将被打断。

正常执行事务并监控

 127.0.0.1:6379> set money 100  #模拟账户余额
 OK
 127.0.0.1:6379> set out 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  #账户余额减少20
 QUEUED
 127.0.0.1:6379> INCRBY out 20  #支出金额变为20
 QUEUED
 127.0.0.1:6379> EXEC  #执行事务,事务正常执行
 1) (integer) 80
 2) (integer) 20

模拟事务执行前key被改动

1、监控money,在事务队列中放入指令,先不执行:

 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 out 10
 QUEUED

2、开启一个redis 客户端,修改money 这个key的值:

 127.0.0.1:6379> get money
 "80"
 127.0.0.1:6379> set money 1000
 OK
 127.0.0.1:6379> get money
 "1000"

3、执行事务,事务执行失败:

 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 out 10
 QUEUED
 127.0.0.1:6379> EXEC
 (nil)

watch指令类似于乐观锁,在事务提交时,如果watch监控的多个KEY中任何KEY的值已经被其他客户端更改,则使用EXEC执行事务时,事务队列将不会被执行,同时返回Nullmulti-bulk应答以通知调用者事务执行失败。

unwatch

Unwatch 命令用于取消 WATCH 命令对所有 key 的监视。

 127.0.0.1:6379> watch money
 OK
 127.0.0.1:6379> unwatch
 OK

\