6-Redis事务与锁

事务

ACID 要么同时成功 要么同时失败

事务等于一组命令的集合

相当于一组命令的队列 事务中的所有命令都会被序列化

会按照顺序 一次性 顺序执行 ,执行过程中不会被打断。

Redis的事务没有隔离级别的概念 所以不会出现传统数据库隔离级别(脏读、幻读、不可重复读)等情况

Redisa单条命令保证原子性 Redis事务不保证原子性。

所有的redis命令在事务中 并没有被直接执行,只有真正执行到这条命令的时候才执行

redis 事务:

  1. 开启事务(multi)
  2. 命令入队(具体命令)
  3. 执行事务(exec)

开启一个事务

127.0.0.1:6379> MULTI #开启事务
OK
127.0.0.1:6379> set key1 v1
QUEUED #进入队列
127.0.0.1:6379> set key2 v2
QUEUED
127.0.0.1:6379> get key1
QUEUED
127.0.0.1:6379> set key3 v3
QUEUED
127.0.0.1:6379> exec #执行事务
1) OK #key1 v1 创建成功
2) OK #key2 v2 创建成功
3) "v1"  #get key1
4) OK #key3 v3创建成功

取消一个事务

执行完了 这个事务就没有了 如果需要 需要再次开启

127.0.0.1:6379> MULTI #上面执行完成事务就没有了  需要再次开启事务
OK
127.0.0.1:6379> set key1 v1
QUEUED #进入队列
127.0.0.1:6379> set key2 v2
QUEUED
127.0.0.1:6379> set key5 v5
QUEUED
127.0.0.1:6379> DISCARD #取消事务
OK
127.0.0.1:6379> get key4 #key4 没有创建成功
(nil)

事务语法错误

整个事务不会被执行 有点像java的编译错误 代码语法有问题 编译不通过 不会生成.class文件

整个都不会编译成功

127.0.0.1:6379> MULTI #开启事务
OK
127.0.0.1:6379> set key1 v1
QUEUED #进入队列
127.0.0.1:6379> set key2 v2
QUEUED
127.0.0.1:6379> getou key3 v3 #队列中有一个命令是错误的
(error) ERR unknown command `getou`, with args beginning with: `key3`, `v3`, 
127.0.0.1:6379> set key4 v4
QUEUED
127.0.0.1:6379> exec #执行事务的时候报错 整个事务不会被执行
(error) EXECABORT Transaction discarded because of previous errors.
127.0.0.1:6379> get key1 #  查询不到创建结果  所有命令没有执行
(nil)

事务逻辑错误

语法没问题 逻辑上的问题 只有逻辑错误的语句不执行,其它语句正常执行

像java运行错误,比如内存溢出问题 编译的时候不会出问题 运行也不会出问题 只有当内存真的溢出了才会有故障。

127.0.0.1:6379> MULTI #开启事务
OK
127.0.0.1:6379> set key1 "测试"
QUEUED #进入队列
127.0.0.1:6379> incr key1 #key1自增 这个是错误的 字符串没法自增
QUEUED
127.0.0.1:6379> set key2 v2
QUEUED
127.0.0.1:6379> set key3 v3
QUEUED
127.0.0.1:6379> get key3
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
5) "v3"
127.0.0.1:6379> get key3 #可以查询到key3的值
"v3"
127.0.0.1:6379> 

监控watch

  1. 悲观锁 认为一直都会出问题 比较谨慎 做什么都先加锁 性能比较差

  2. 乐观锁 认为一直都不会出问题 所以不会上锁 更新数据的时候判断一下 是否有人修改过数据

    2.1 获取version

    2.2 更新的时候比较version

    127.0.0.1:6379> set num 1000
    OK
    127.0.0.1:6379> set outmum 0
    OK
    127.0.0.1:6379> watch num #开启监控
    OK
    127.0.0.1:6379> MULTI #开启事务
    OK
    127.0.0.1:6379> DECRBY mun 20
    QUEUED
    127.0.0.1:6379> INCRBY outmun 20
    QUEUED
    127.0.0.1:6379> exec #事务正常执行
    1) (integer) -20
    2) (integer) 20
    127.0.0.1:6379> 
    

    image-20220419111726172

    127.0.0.1:6379>  set num 1000
    OK
    127.0.0.1:6379> set outnum 0
    OK
    127.0.0.1:6379> watch num
    OK
    127.0.0.1:6379> MULTI
    OK
    127.0.0.1:6379> DECRBY money 30
    QUEUED
    127.0.0.1:6379> INCRBY outnum 30
    QUEUED
    127.0.0.1:6379> 
    

    窗口2在窗口一事务还未执行的情况下 修改了num

    image-20220419111933631

    127.0.0.1:6379> get num
    "1000"
    127.0.0.1:6379> set num 100
    OK
    127.0.0.1:6379> 
    
    

    返回窗口1 执行事务失败返回nil

    image-20220419112205147

    结论:在执行事务之前 另外一个线程修改了我们需要的值 这时候会导致事务执行失败

    乐观锁

    利用watch命令监听key,实现乐观锁 应用场景比如秒杀来防止超卖

    127.0.0.1:6379>unwatch  #如果执行失败  就先解锁
    OK
    127.0.0.1:6379>watch num  #在重新上锁 获取最新的值
    OK
    127.0.0.1:6379> MULTI #开启事务 以上三步 如果失败  重新操作
    OK
    127.0.0.1:6379> DECRBY money 30
    QUEUED
    127.0.0.1:6379> INCRBY outnum 30
    QUEUED
    127.0.0.1:6379> exec
    
posted @ 2022-04-21 11:11  机猿巧合  阅读(26)  评论(0编辑  收藏  举报