一、Redis事务的本质
一组命令的集合,一个事务中的所有命令都会被序列化,在事务执行过程中,会按照顺序执行。
------头部 -> set -> set -> set -> 尾部------
特性:
- 一次性
- 顺序性
- 排他性
Redis事务没有隔离级别的概念。
Redis单条指令是保证原子性的,但是事务不保证原子性。
二、Redis事务实践
主要命令如下:
- MULTI ->开启事务
- EXEC ->执行事务
- DISCARD ->放弃事务
执行效果如下所示
1.正常执行事务
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> EXEC #执行事务
1) OK
2) OK
3) "v1"
4) "v2"
127.0.0.1:6379>
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> DISCARD #放弃事务
OK
127.0.0.1:6379> get k1 #事务已经被放弃,不会取到值
(nil)
127.0.0.1:6379> get k2
(nil)
3.编译时发生错误
当事务开启之后,多条命令中包含一条不存在的命令,本次事务会全部放弃。
127.0.0.1:6379> MULTI #开启事务
OK
127.0.0.1:6379> set k1 v1
QUEUED
127.0.0.1:6379> setget k2 v2 #此处故意输入错误命令,编译时已经报错
(error) ERR unknown command `setget`, with args beginning with: `k2`, `v2`,
127.0.0.1:6379> EXEC #执行事务,事务全部放弃,不会成功
(error) EXECABORT Transaction discarded because of previous errors.
127.0.0.1:6379> get k1 #没有取到编译错误之前设置的值
(nil)
4.执行时发生错误
当事务开启之后,某些命令执行了不合理的操作,那么该命令不会生效,但不影响本次事务中其他命令的执行。
127.0.0.1:6379> set k1 wuhan
OK
127.0.0.1:6379> get k1
"wuhan"
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> set k2 shanghai
QUEUED
127.0.0.1:6379> INCR k1
QUEUED
127.0.0.1:6379> set k3 beijing
QUEUED
127.0.0.1:6379> EXEC
1) OK
2) (error) ERR value is not an integer or out of range #此处k1不是数字,执行incr会报错
3) OK
127.0.0.1:6379> get k1
"wuhan"
127.0.0.1:6379> get k2 #虽然执行命令INCR k1时出错,没有影响其余正常命令的执行
"shanghai"
127.0.0.1:6379> get k3
"beijing"
三、Redis实现乐观锁
乐观锁
- 做事很乐观,认为什么时候都不会出问题,所以不会真正上锁。在更新数据的时候去判断一下,在此期间内是否有人修改过数据。
- 获取version
- 更新的时候比较version
Redis中如何实现?
WATCH key [key ...]
在操作之前监视指定的key,如果事务执行期间被别的线程修改过,那么整个事务将会失效。
线程1:
127.0.0.1:6379> set money 1000 #假设账户有1000元
OK
127.0.0.1:6379> WATCH money #取之前先监视key
OK
127.0.0.1:6379> MULTI #开启事务
OK
127.0.0.1:6379(TX)> DECRBY money 500 #取500
QUEUED
127.0.0.1:6379(TX)> EXEC #执行事务,返回结果为(nil),代表事务执行失败
(nil)
127.0.0.1:6379> get money #查看金额已经发生变动
"600"
线程2:
127.0.0.1:6379> get money #账户起始金额为1000
"1000"
127.0.0.1:6379> DECRBY money 400 #取400,余额为600
(integer) 600