redis 事务的介绍
- Redis事务⼀次可以执⾏多条命令,按照命令的顺序进⾏串⾏化执⾏,执⾏命令的时候不允许其他命令插⼊,不许加塞。(保证原⼦性)
- ⼀个 Redis 事务从开始(MULTI)到执⾏(EXEC)的过程,这段时间内所有的命令都会被序列化、⼊队,在 EXEC 命令被调⽤时⼀次性、按顺序地执⾏
- 事务的状态变化:开始事务、命令⼊队、执⾏事务
- Redis 事务可以通过以下四个命令来实现:
MULTI
:它标记了⼀个事务块的开始。MULTI
命令之后的所有命令不会⽴即执⾏,⽽是缓存在服务器的⼀个事务队列中,然后当EXEC
命令被调⽤时⼀次性、按顺序地执⾏EXEC
:它标记事务的提交,它的作⽤是触发服务器执⾏所有在MULTI
之后⼊队的命令。DISCARD
:如果客户端在MULTI
之后决定取消事务,那么它可以调⽤DISCARD
命令来做这件事。调⽤DISCARD
之后,服务器会清空事务队列,并将客户端的状态从事务状态调整回⾮事务状态WATCH key [key ...]
:WATCH
命令⽤于在执⾏事务前监控⼀个或多个键,以此来达到乐观锁的效果。如果在调⽤EXEC
命令执⾏事务之前,有其他客户端抢先对任何⼀个被监视的键进⾏了替换、更新、删除等操作,那么当客户端尝试执⾏事务时,服务器将返回⼀个错误,客户端可以在这时选择重试事务或者放弃事务。
- Redis 的事务不⽀持回滚,即如果事务执⾏过程中出现错误,Redis 只是简单地停⽌执⾏后续的命令,但是不会回滚已经执⾏的命令。
正常演示
# 简单演示 正常提交事务
127.0.0.1:6379> MULTI # 开启事务标记
OK
127.0.0.1:6379(TX)> set num 10
QUEUED
127.0.0.1:6379(TX)> get num
QUEUED
127.0.0.1:6379(TX)> EXEC # 事务提交
1) OK
2) "10"
# 简单演示 取消事务的执⾏
127.0.0.1:6379> MULTI # 开启事务
OK
127.0.0.1:6379(TX)> set num1 20
QUEUED
127.0.0.1:6379(TX)> set num2 100
QUEUED
127.0.0.1:6379(TX)> get num1
QUEUED
127.0.0.1:6379(TX)> get num2
QUEUED
127.0.0.1:6379(TX)> DISCARD # 事务取消
OK
失败演示
Redis事务会将命令按照顺序执⾏串⾏化操作,但是如果这些命令中有⼀个命令失败了是否会影响到整个事务的执⾏,也就是事务中断或者取消?
答案:不会,只有错误(⾮语法错误)的命令不会执⾏
# 在⼀个redis事务中,如果出现了错误的命令,只有错误的命令不会执⾏,但不会影响整体事务执⾏
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379(TX)> set num 10
QUEUED
127.0.0.1:6379(TX)> set str abc
QUEUED
127.0.0.1:6379(TX)> INCR num
QUEUED
127.0.0.1:6379(TX)> INCR str # 错误的命令
QUEUED
127.0.0.1:6379(TX)> get num
QUEUED
127.0.0.1:6379(TX)> get str
QUEUED
127.0.0.1:6379(TX)> exec
1) OK
2) OK
3) (integer) 11
4) (error) ERR value is not an integer or out of range # 错误命令不执⾏
5) "11"
6) "abc"
127.0.0.1:6379> keys *
1) "str"
2) "num"
Redis事务中,如果我们的命令语法都出现了错误,那么Redis事务还会执⾏吗?不会执行事务
# 此案例中出现了语法错误
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379(TX)> set num1 20
QUEUED
127.0.0.1:6379(TX)> get num1
QUEUED
127.0.0.1:6379(TX)> INCR num1 10 # 语法错误会打断事务执⾏
(error) ERR wrong number of arguments for 'incr' command
127.0.0.1:6379(TX)> EXEC
(error) EXECABORT Transaction discarded because of previous
errors.
127.0.0.1:6379> keys * # 没有num1
1) "str"
2) "num"
watch命令
WATCH 是 Redis 的⼀个事务命令,它为 Redis 提供了乐观锁(Optimistic Locking)的功能。这使得你可以在执⾏事务时添加⼀种并发控制机制
秒杀业务就是通过Redis的watch命令来实现乐观锁实现功能
在 Redis 中, WATCH 命令可以⽤来监视⼀个或多个 key,如果在事务执⾏之前这些key 的值发⽣了改变,那么事务将会被打断。
注意:⼀旦执⾏了 EXEC 或 DISCARD 命令,所有的 WATCH 监控都会被取消。
⼯作流程:
- WATCH ⼀个或多个 key。
- 创建事务,即发送 MULTI 命令。
- 发送⼀系列的命令。
- 执⾏事务,即发送 EXEC 命令。此时,如果任何被 WATCH 的 key ⾃你开始WATCH 以来已被其他客户端更改,那么 EXEC 将失败,事务被打断。
# 此案例演示的是并发时出现的线程安全问题
# 客户端1 操作
# 案例:账户a有100元 b有50元,现在要求将a账户的100元转到b账户,最终结果 a=0
b=150
127.0.0.1:6379> set a 100
OK
127.0.0.1:6379> set b 50
OK
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379(TX)> DECRBY a 100
QUEUED
127.0.0.1:6379(TX)> INCRBY b 100
QUEUED
127.0.0.1:6379(TX)> get a
QUEUED
127.0.0.1:6379(TX)> get b
QUEUED
127.0.0.1:6379(TX)> EXEC # 执⾏事务提交之前出现了另外的线程修改了a账户数据
的情况
1) (integer) -50 # a账户结果有误
2) (integer) 150
3) "-50"
4) "150"
# 客户端2 操作
# 在客户端1提交事务之前,减少a账户⾦额为50
127.0.0.1:6379> get a
"100"
127.0.0.1:6379> get b
"50"
127.0.0.1:6379> DECRBY a 50
(integer) 50
127.0.0.1:6379> get a
"50"
127.0.0.1:6379> get b
"50"
为了解决以上的并发安全问题,我们可以采⽤watch命令来监控a和b账户 就是监控key
# 客户端1
# 需要通过watch命令监控 a与b,避免发⽣线程安全问题
127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> set a 100
OK
127.0.0.1:6379> set b 50
OK
127.0.0.1:6379> WATCH a b # 开启监控 key a与b
OK
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379(TX)> DECRBY a 100
QUEUED
127.0.0.1:6379(TX)> INCRBY b 50
QUEUED
127.0.0.1:6379(TX)> get a
QUEUED
127.0.0.1:6379(TX)> get b
QUEUED
127.0.0.1:6379(TX)> EXEC # 在提交之前客户端2 改变了a的值,出现并发安全问题,因为有watch
# 监控所以事务提交不成功,避免发⽣线程安全问题
(nil)
# 客户端2
# 在客户端1事务提交之前,改变a账户的值
127.0.0.1:6379> get a
"100"
127.0.0.1:6379> get b
"50"
127.0.0.1:6379> DECRBY a 50
(integer) 50
127.0.0.1:6379> get a
"50"
127.0.0.1:6379> get b
"50"
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 1909773034@qq.com