“这是我参与8月更文挑战的第14天,活动详情查看:8月更文挑战”
1、事务概述
事务的本质
单个 Redis 命令的执行是原子性的,但 Redis 没有在事务上增加任何维持原子性的机制,所以 Redis 事务的执行并不是原子性的。
事务可以理解为一个打包的批量执行脚本,但批量指令并非原子化的操作,中间某条指令的失败不会导致前面已做指令的回滚,也不会造成后续的指令不做。
总结说:redis事务就是一次性、顺序性、排他性的执行一个队列中的一系列命令。
事务执行过程
Redis提供的事务是将多个命令打包,然后一次性、按照先进先出的顺序(FIFO)有序的执行。在执行过程中不会被打断(在事务执行过程,其他客户端提交的命令请求不会插入到事务执行命令序列中),当事务队列中的所以命令都被执行(无论成功还是失败)完毕之后,事务才会结束。
先以 MULTI 开始一个事务, 然后将多个命令入队到事务中, 最后由 EXEC 命令触发事务, 一并执行事务中的所有命令:
- 开启事务(MULTI)
- 命令入队
- 执行事务(EXEC)
redis 事务命令
下表列出了 redis 事务的相关命令:
序号 | 命令及描述 |
---|---|
1 | DISCARD 取消事务,放弃执行事务块内的所有命令。 |
2 | EXEC 执行所有事务块内的命令。 |
3 | MULTI 标记一个事务块的开始。 |
4 | UNWATCH 取消 WATCH 命令对所有 key 的监视。 |
5 | WATCH 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
\