美文网首页
【Redis 系列】redis 学习六,redis 事务处理和监

【Redis 系列】redis 学习六,redis 事务处理和监

作者: 阿兵云原生 | 来源:发表于2022-02-26 22:09 被阅读0次

    【Redis 系列】redis 学习六,redis 事务处理和监控事务

    写在前面

    我们学过的事务都是保证原子性的,但是 redis 的事务中执行多个指令,是不保证原子性的

    redis 事务的本质

    就是一组命令的集合,一个事务中所有的命令都会被序列化,在事务执行的过程,是按照顺序执行命令的,他们拥有

    • 一次性
    • 顺序性
    • 排他性

    redis 的事务没有隔离级别的概念

    redis 事务中,命令是这样执行的

    命令放在事务中,并没有马上执行,而是发起执行命令的时候才会执行,通过 exec 触发

    image

    redis 是单条指令保证原子性,但是事务不保证原子性

    执行一个事务的流程是这个样子的:

    • multi 开启事务
    • 各种命令入队
    • exec 执行事务

    开启事务

    MULTI

    开启一个事务

    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)> set name xiaozhu
    QUEUED
    127.0.0.1:6379(TX)> set age 19
    QUEUED
    127.0.0.1:6379(TX)> set hobby paly
    QUEUED
    127.0.0.1:6379(TX)> exec
    1) OK
    2) OK
    3) OK
    4) OK
    5) OK
    127.0.0.1:6379> MULTI
    OK
    127.0.0.1:6379(TX)> set city changsha
    QUEUED
    127.0.0.1:6379(TX)> get city
    QUEUED
    127.0.0.1:6379(TX)> exec
    1) OK
    2) "changsha"
    

    放弃事务

    DISCARD

    放弃最近开启的事务

    127.0.0.1:6379> flushdb
    OK
    127.0.0.1:6379> MULTI
    OK
    127.0.0.1:6379(TX)> set name xiaozhu
    QUEUED
    127.0.0.1:6379(TX)> set age 10
    QUEUED
    127.0.0.1:6379(TX)> set city beijing
    QUEUED
    127.0.0.1:6379(TX)> DISCARD
    OK
    127.0.0.1:6379> get city
    (nil)
    127.0.0.1:6379> get name
    (nil)
    
    

    事务出错

    事务出错分为两种:

    • 编译型出错

    • 运行时出错

    编译型出错

    代码编写出错,无法通过编译,此时事务里面编写的指令,全部执行失败

    127.0.0.1:6379> flushdb
    OK
    127.0.0.1:6379> multi
    OK
    127.0.0.1:6379(TX)> set name xiaozhu
    QUEUED
    127.0.0.1:6379(TX)> set age 10
    QUEUED
    127.0.0.1:6379(TX)> set hobby play
    QUEUED
    127.0.0.1:6379(TX)> set city changsha
    QUEUED
    127.0.0.1:6379(TX)> set dream xxx
    QUEUED
    127.0.0.1:6379(TX)> setget hahaha  # 指令不存在,编译不通过,直接报错
    (error) ERR unknown command `setget`, with args beginning with: `hahaha`,
    127.0.0.1:6379(TX)> exec
    (error) EXECABORT Transaction discarded because of previous errors.
    127.0.0.1:6379> get city
    (nil)
    

    一旦编译出错,事务里面的所有指令都不会执行

    运行时出错

    程序运行时某一个指定的某个逻辑出现问题,仅仅影响本条执行的执行,并且本条指令会跑出异常,不影响事务中其他指令的执行

    127.0.0.1:6379> flushdb
    OK
    127.0.0.1:6379> set name xiaozhu
    OK
    127.0.0.1:6379> multi
    OK
    127.0.0.1:6379(TX)> get name
    QUEUED
    127.0.0.1:6379(TX)> INCR name  # incr 指令用法正确,只不过 name 是字符串,运行的时候,是无法执行通过的
    QUEUED
    127.0.0.1:6379(TX)> set age 29
    QUEUED
    127.0.0.1:6379(TX)> set hobby play
    QUEUED
    127.0.0.1:6379(TX)> exec
    1) "xiaozhu"
    2) (error) ERR value is not an integer or out of range
    3) OK
    4) OK
    127.0.0.1:6379> get hobby
    "play"
    127.0.0.1:6379> get name
    "xiaozhu"
    

    根据上述结果,incr 指令的用法正确,但是 incr 只能对数字进行操作,name 是一个字符串,无法做自增操作,因此运行时报错,事务中不影响其他的指令执行

    redis 的 watch 监控

    image

    watch 监控,可以说说 乐观锁 和 悲观锁

    乐观锁

    • 很乐观,认为什么时候都不会出问题,所以不会上锁,就更新数据的时候会去判断一下在此期间若数据发生了变化
    • 乐观锁会先获取一个基础数据版本
    • 更新数据的时候,会比较这个版本是否发生变化,若发生变化,则更新失败,若未发生变化,则更新数据

    悲观锁

    • 很悲观,无论什么时候都认为会出问题,都要加锁

    redis 的监控测试

    用 watch 来加锁模拟银行取钱

    • money 账户 有 2000
    • outer 账户 有 500
    • money 账户 给 outer 账户 500
    127.0.0.1:6379> flushall
    OK
    127.0.0.1:6379> set money 2000
    OK
    127.0.0.1:6379> set outer 500
    OK
    127.0.0.1:6379> watch money
    OK
    127.0.0.1:6379> multi
    OK
    127.0.0.1:6379(TX)> DECRBY money 500
    QUEUED
    127.0.0.1:6379(TX)> INCRBY outer 500
    QUEUED
    127.0.0.1:6379(TX)> exec
    1) (integer) 1500
    2) (integer) 1000
    

    开始测试模拟多个线程来操作

    redis 客户端 1

    • money 账户 有 2000
    • outer 账户 有 500
    • money 账户 给 outer 账户 500 ,还未开始执行
    127.0.0.1:6379> set money 2000
    OK
    127.0.0.1:6379> set outer 500
    OK
    127.0.0.1:6379> watch money
    OK
    127.0.0.1:6379> multi
    OK
    127.0.0.1:6379(TX)> DECRBY money 500
    QUEUED
    127.0.0.1:6379(TX)> INCRBY outer 500
    QUEUED
    

    客户端 1 的事务操作还未开始执行,客户端 2 就进行了如下操作

    127.0.0.1:6379> set money 10000
    OK
    127.0.0.1:6379> set money 10000
    OK
    

    此时执行客户端 1 的 exec 指令

    127.0.0.1:6379(TX)> exec
    (nil)
    127.0.0.1:6379> get money
    "10000"
    

    发现客户端 1 执行 exec 的时候,是一个 nil ,说明变更失败,是因为 watch 监控到 money 有变动,因此事务执行失败

    unwatch

    发现事务执行失败了,需要使用命令 unwatch 解除监控

    127.0.0.1:6379> 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 1000
    QUEUED
    127.0.0.1:6379(TX)> INCRBY outer 1000
    QUEUED
    127.0.0.1:6379(TX)> exec
    1) (integer) 9000
    2) (integer) 1500
    

    这就是 redis 实现乐观锁的操作,一般使用场景会放到秒杀系统里面进行应用

    参考资料:

    redis_doc

    欢迎点赞,关注,收藏

    朋友们,你的支持和鼓励,是我坚持分享,提高质量的动力

    image

    好了,本次就到这里

    技术是开放的,我们的心态,更应是开放的。拥抱变化,向阳而生,努力向前行。

    我是小魔童哪吒,欢迎点赞关注收藏,下次见~

    相关文章

      网友评论

          本文标题:【Redis 系列】redis 学习六,redis 事务处理和监

          本文链接:https://www.haomeiwen.com/subject/ikjcrrtx.html