前言
Redis事务可以一次执行多个命令(按顺序地串行执行,执行中不会被其他命令插入,不许加塞)
1.简介
Redis事务可以一次执行多个命令(允许在一次单独的步骤中执行一组命令)。
特征:
[1]批量操作在发送EXEC命令前被放入队列缓存
[2]收到EXEC命令后进入事务执行,事务中任意命令执行失败,其余的命令依然被执行
[3]在事务执行过程中,其他客户端提交的命令请求不会被插入到事务执行命令序列中
因此Redis事务有如下保证:
[1]Redis会将一个事务中的所有命令序列化,然后顺序执行
[2]执行中不会被其他命令插入,不许出现加塞行为
2.事务的处理的阶段与错误处理机制
[1] 三个阶段:
开始事务
命令入队
执行事务
[2]错误处理机制:
队列中的某个命令出现报告错误(即不存在的命令),执行时整个的所有队列都会被取消。
3.相关命令
DISCARD # 取消事务,放弃执行事务块内的所有命令
EXEC # 执行所有事务块内的命令
MULTI # 标记一个事务的开始
UNWATCH # 取消WATCH命令对所有key的监视
WATCH # 监视一个(或多个)key,如果在事务执行之前这个(或这些)key被其他命令所改动,那么事务将被打断
4.相关示例
[1] Multi 和 Exec
需求: 转账功能, A向B账号转账50元
这是一个事务的例子,它先以Multi 开启一个事务,然后多个命令入队到事务中,最后由Exec命令触发事务
代码示例:
127.0.0.1:6379[1]> set account:a 80
OK
127.0.0.1:6379[1]> set account:b 10
OK
127.0.0.1:6379[1]> multi
OK
127.0.0.1:6379[1]> get account:a
QUEUED
127.0.0.1:6379[1]> get account:b
QUEUED
127.0.0.1:6379[1]> decrby account:a 50
QUEUED
127.0.0.1:6379[1]> incrby account:b 50
QUEUED
127.0.0.1:6379[1]> exec
1) "80"
2) "10"
3) (integer) 30
4) (integer) 60
127.0.0.1:6379[1]> get account:a
"30"
127.0.0.1:6379[1]> get account:b
"60"
解析说明:
1] 输入Multi命令开始,输入的命令都会依次进入命令队列中,但不会执行
2] 直到输入Exec后,Redis会将之前队列中的命令依次执行。
[2] Discard 放弃队列运行
127.0.0.1:6379[1]> set count:a 80
OK
127.0.0.1:6379[1]> set count:b 10
OK
127.0.0.1:6379[1]> multi
OK
127.0.0.1:6379[1]> get count:a
QUEUED
127.0.0.1:6379[1]> get count:b
QUEUED
127.0.0.1:6379[1]> decrby count:a 50
QUEUED
127.0.0.1:6379[1]> incrby count:b 50
QUEUED
127.0.0.1:6379[1]> discard
OK
127.0.0.1:6379[1]> get count:a
"80"
127.0.0.1:6379[1]> get count:b
"10"
解析说明:
1] 输入Multi 命令开始,输入的命令都会依次进入命令队列中,但不会执行
2] 直到输入Exec 后, Redis会将之前的命令对列中的命令依次执行
3] 命令队列的过程中可以通过discard来放弃队列运行。
[3] 事务的错误处理
1) 事务的错误处理:
如果执行的某个命令报出了错误(非不存在的命令),则只有报错的命令不会被执行,而其他的命令都会执行,
不会回滚。
代码示例:
127.0.0.1:6379[1]> multi
OK
127.0.0.1:6379[1]> set hello hello
QUEUED
127.0.0.1:6379[1]> incr hello #错误的命令
QUEUED
127.0.0.1:6379[1]> get hello
QUEUED
127.0.0.1:6379[1]> exec
1) OK
2) (error) ERR value is not an integer or out of range
3) "hello"
2) 事务的错误处理:
队列中的某个命令出现了报告错误(不存在的命令),执行时整个的所有队列都会被取消
代码示例:
127.0.0.1:6379[1]> multi
OK
127.0.0.1:6379[1]> set a 111
QUEUED
127.0.0.1:6379[1]> abc # 命令出现报告错误
(error) ERR unknown command 'abc'
127.0.0.1:6379[1]> get a
QUEUED
127.0.0.1:6379[1]> exec
(error) EXECABORT Transaction discarded because of previous errors.
[4] 事务的Watch
Watch key[key…]
监视一个(或多个)key,如果在事务执行之前这个(或这些)key被其他命令所改动,那么事务将被打断。
需求:某一账户在一事务内进行操作,在提交事务前,另一个进程对该账户进行操作
我们开启两个窗口(假设有两个用户张三和李四同时对count:b的值进行修改)
- 张三在进行操作前先对 count:b加了watch的操作
- 与此同时,李四在张三执行完 watch count:b 时,也对count:b进行了修改
- 张三在继续执行自己的事务
可以看到最终事务是被终止了,且count:b为李四修改后的值
[5] UNWATCH
Redis 中的Unwatch 命令用于取消WATCH命令对所有key的监视
如果在执行WATCH 命令之后, EXEC命令或DISCARD命令先执行的话,那就不需要执行UNWATCH了。
127.0.0.1:6379[1]> watch count:b
OK
127.0.0.1:6379[1]> unwatch
OK
127.0.0.1:6379[1]> incrby count:b 10 #这时对数据进行修改也不会造成事务的执行失败
(integer) 50
127.0.0.1:6379[1]> multi
OK
127.0.0.1:6379[1]> incrby count:b 10
QUEUED
127.0.0.1:6379[1]> get count:b
QUEUED
127.0.0.1:6379[1]> exec
1) (integer) 60
2) "60"
5.应用场景
[1] 一组命令必须同时都执行,或者都不执行
[2] 我们想要保证一组命令在执行的过程之中不被其他命令插入
[3] 商品秒杀(活动)
[4] 转账活动