Redis 笔记(十一)-事务及 redis 乐观锁
2021-08-20 本文已影响0人
yjtuuige
- 事务隔离原则
atomicity
:原子性(要么全部执行,要么全都不执行)
consistency
:一致性
isolation
:隔离性
durability
:持久性 -
redis
单条命令是保证原子性的,但是事务是不保证原子性的 -
redis
事务没有隔离级别的概念 -
redis
事务的本质:一组命令的集合,在执行过程中是按照队列的顺序依次执行
--- 队列 set set set 执行---
- 事务中每条命令都会被序列化,执行过程中按顺序执行,不允许其他命令进行干扰
一次性、顺序性、排他性 -
redis
事务操作过程:
1、开启事务:multi
2、命令入队:……
3、执行事务:exec
127.0.0.1:6379> multi # 开启事务
OK
127.0.0.1:6379(TX)> set k1 v1 # 命令入队
QUEUED
127.0.0.1:6379(TX)> set k2 v2
QUEUED
127.0.0.1:6379(TX)> get k1
QUEUED
127.0.0.1:6379(TX)> get k2
QUEUED
127.0.0.1:6379(TX)> keys *
QUEUED
127.0.0.1:6379(TX)> exec # 执行事务
1) OK
2) OK
3) "v1"
4) "v2"
5) 1) "k2"
2) "k1"
- 取消事务:
discard
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> set k1 v1
QUEUED
127.0.0.1:6379(TX)> set k2 v2
QUEUED
127.0.0.1:6379(TX)> discard # 放弃事务
OK
127.0.0.1:6379> exec
(error) ERR EXEC without MULTI # 当前未开启事务
127.0.0.1:6379> get k1 # 被放弃事务中,命令并未执行
(nil)
- 事务错误:
1、编译时异常:代码语法错误,所有的命令都不执行
2、运行时异常:代码逻辑错误 ,其他命令可以正常执行127.0.0.1:6379> multi OK 127.0.0.1:6379(TX)> set k1 v1 QUEUED 127.0.0.1:6379(TX)> get k1 v1 # 错误语法 (error) ERR wrong number of arguments for 'get' command # 会报错但是不影响后续命令入队 127.0.0.1:6379(TX)> keys * QUEUED 127.0.0.1:6379(TX)> 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(TX)> set k1 v1 QUEUED 127.0.0.1:6379(TX)> set k2 v2 QUEUED 127.0.0.1:6379(TX)> incr k2 # 逻辑错误(对字符串进行增量) QUEUED 127.0.0.1:6379(TX)> get k1 QUEUED 127.0.0.1:6379(TX)> exec 1) OK 2) OK 3) (error) ERR value is not an integer or out of range # 运行时报错 4) "v1" # 其他命令正常执行
虽然中间有一条命令报错了,但是后面的指令依旧正常执行成功了。
所以说Redis
单条指令保证原子性,但是Redis
事务不能保证原子性。
-
监控:
watch
1、悲观锁:很悲观,认为什么时候都会出现问题,无论做什么都会加锁
2、乐观锁:- 很乐观,认为什么时候都不会出现问题,所以不会上锁!更新数据的时候去判断一下,在此期间是否有人修改过这个数据
- 获取
version
,更新的时候比较version
- 使用
watch key
监控指定数据,相当于乐观锁加锁
-
redis
监视测试:
1、正常执行127.0.0.1:6379> set money 100 # 设置余额:100 OK 127.0.0.1:6379> set use 0 # 支出使用:0 OK 127.0.0.1:6379> watch money # 监视 money (上锁) OK 127.0.0.1:6379> multi OK 127.0.0.1:6379(TX)> decrby money 10 QUEUED 127.0.0.1:6379(TX)> incrby use 10 QUEUED 127.0.0.1:6379(TX)> exec # 监视值没有被中途修改,事务正常执行 1) (integer) 90 2) (integer) 10
2、测试多线程修改值,使用
watch
可以当做redis
的乐观锁操作(相当于get version
),启动另外一个客户端模拟插队线程。
线程1:127.0.0.1:6379> watch money # money 上锁 OK 127.0.0.1:6379> multi OK 127.0.0.1:6379(TX)> decrby money 30 QUEUED 127.0.0.1:6379(TX)> incrby use 30 QUEUED 127.0.0.1:6379(TX)> # 此时事务并没有执行,切换到线程2执行
模拟线程插队,线程2:
127.0.0.1:6379> incrby money 100 # 修改了线程一中监视的 money (integer) 190
回到线程1,执行事务:
127.0.0.1:6379(TX)> exec # 执行之前,因另一个线程修改了值,会导致事务执行失败 (nil)
3、
redis
乐观锁,可以通过watch
关键字来监视字段。
当执行事务之前会比较修改值之前,和监视的值是否相同,如果相同则执行事务成功,否则执行事务失败,返回nil
。
使用unwatch
关键字来解锁相关字段,再去加锁获取最新的值。
回到线程1:127.0.0.1:6379> unwatch # 发现事务执行失败,先使用 unwatch 解锁 OK 127.0.0.1:6379> watch money # 获取最新的值,再次监视 OK 127.0.0.1:6379> multi OK 127.0.0.1:6379(TX)> decrby money 30 QUEUED 127.0.0.1:6379(TX)> incrby use 30 QUEUED 127.0.0.1:6379(TX)> exec # 对比监视的值,如果没有发生变化,执行成功,否则失败 1) (integer) 160 2) (integer) 40