码农知识堂 - 1000bd
  •   Python
  •   PHP
  •   JS/TS
  •   JAVA
  •   C/C++
  •   C#
  •   GO
  •   Kotlin
  •   Swift
  • Redis事务+秒杀案例


            Redis事务是一个单独的隔离操作,是指将多条命令放在一个命令队列当中,按顺序执行,保证多个命令在同一个事务中执行而不受其他客户端的影响。

            通俗来说就是:串联多个命令防止别的命令插队。

    1.Multi、Exec、discard

            在输入Multi命令后,输入的命令都会依次进入命令队列中,这一过程也叫组队,直到输入Exec后,Redis会将命令队列中的命令依次执行。如下示例:

            而且在组队的过程中可以通过discard来退出输入,结束这个事务。如下示例:  

     

    2.事务错误的处理

    2.1组队的过程中报错

            如果组队的过程中添加命令时出现了错误,那么输入exec执行时队列中所有的命令都不会执行。  

    2.2执行过程中报错

    redis的事务中不会保证原子性,其中第二条命令有误,并不会影响命令1和命令3的执行。

    3.Redis怎么解决事务冲突问题(watch)

            比如高并发下多个请求同时操作同一个账户,那么账户余额就可能出现问题,正常情况可以采用悲观锁或者乐观锁来解决。其中悲观锁实现就是给账户余额加锁,同一时间只能由一个事务来操作。乐观锁实现就是通过版本号来控制。但是乐观锁适用于读多写少的场景,而Redis就是利用乐观锁这种CAS机制来解决事务冲突问题的。

    3.1对watch我的理解

            有客户端A和B,客户端A执行了watch key,就代表着A监视着此key,A会保存一份key的当前版本号,如果(无论什么地方)修改了此key的值,那么key的版本号就会发生变更。

            而判断比较key的版本号,这个环节只和事务有关系,只会在multi开启事务后,然后exec执行命令时才会生效。比如多个事务在输入multi命令之前,可以先执行watch key1 ....,用来监视一个或多个key,如果事务A在exec执行时发现事务A监视的key的版本已经被改动,那么这个事务A的命令队列就会被打断,命令全部不会执行,当操作被打断时,会返回空值nil。

    4.Redis事务的三大特性

    • 单独的隔离操作。

    • 没有隔离级别的概念。

    • 不保证原子性,出现错误继续向下执行。

    5.秒杀案例

    我们通过ab工具实现了并发模拟秒杀,结果出现了库存超卖和redis连接超时的问题。以下分析出现原因:

    • 超卖:在高并发请求下,多个请求同时开始查询到的是有库存的,然后就一窝蜂的去减库存,就会导致超卖。

    • 连接超时:因为redis是单线程操作内存,如果一瞬间请求过多,一直排在后面的请求就可能会产生连接超时。

    5.1解决超卖问题

    如果只需解决超卖问题其实也不难,我们可以使用watch来监视库存,从而使用乐观锁解决超卖的问题。

    示例代码(qtkey表示的是库存的键,userkey表示存放用户id的key):

    1. //增加乐观锁,监视库存
    2. jedis.watch(qtkey);
    3. //判断库存
    4. String qtkeystr = jedis.get(qtkey);
    5. if(qtkeystr==null || "".equals(qtkeystr.trim())) {
    6. System.out.println("未初始化库存");
    7. jedis.close();
    8. return false ;
    9. }
    10. int qt = Integer.parseInt(qtkeystr);
    11. if(qt<=0) {
    12. System.err.println("已经秒光");
    13. jedis.close();
    14. return false;
    15. }
    16. //增加事务
    17. Transaction multi = jedis.multi();
    18. //减少库存
    19. multi.decr(qtkey);
    20. //添加用户信息
    21. multi.sadd(usrkey, uid);
    22. //执行事务
    23. List list = multi.exec();
    24. //判断事务提交是否失败
    25. if(list==null || list.size()==0) {
    26. System.out.println("秒杀失败");
    27. jedis.close();
    28. return false;
    29. }
    30. System.err.println("秒杀成功");
    31. jedis.close();
    32. 5.2解决连接超时的问题

              问题就是每个请求都要自己等待去建立和关闭连接,我们使用连接池来解决即可,连接池还能节省每次连接redis服务带来的消耗,能反复利用。

      5.3库存遗留怎么解决

              我们使用乐观锁能够很好的解决超卖的问题,但是无法解决库存遗留的问题,比如库存余额为10,此时有50个请求同时过来,但是由于CAS导致很多的请求都失败了,导致先点的没抢到,后来的反而抢到了。虽然请求很多,但最终都可能导致还有库存遗留,这样就不太好。

              而且redis不支持悲观锁,所以用LUA脚本来解决。

              LUA是一个脚本语言,LUA脚本有一定的原子性,不会被其他命令插队。我们将多步操作,写为一个脚本,一次性的交给redis执行。

              因为redis其单线程的特性,同一时间只能将一个LUA脚本彻彻底底的执行完后,才能执行下一个LUA脚本,所以LUA能同时解决超卖的问题和库存遗留的问题。

    33. 相关阅读:
      全新物联网数据集成 :Flow 可视化编排 & 双向数据桥接
      vue 中的插槽Slot基本使用和具名插槽
      hashcode和equals方法的区别与联系
      学校上课,是耽误我学习了。。
      【前端知识】Three 学习日志(三)—— 光源对物体表面的影响
      《深入浅出.NET框架设计与实现》笔记6.4——ASP.NET Core应用程序多种运行模式之四——服务承载
      PBR系列-物理材质(上)
      C++异常捕获
      大龄转行当程序员:只能选择小众技术,避免与年轻人竞争?
      魔兽世界开服教程——魔兽世界服务器架设全攻略---战网+Ladder排行版
    34. 原文地址:https://blog.csdn.net/m0_62565675/article/details/134540120
      • 最新文章
      • 攻防演习之三天拿下官网站群
        数据安全治理学习——前期安全规划和安全管理体系建设
        企业安全 | 企业内一次钓鱼演练准备过程
        内网渗透测试 | Kerberos协议及其部分攻击手法
        0day的产生 | 不懂代码的"代码审计"
        安装scrcpy-client模块av模块异常,环境问题解决方案
        leetcode hot100【LeetCode 279. 完全平方数】java实现
        OpenWrt下安装Mosquitto
        AnatoMask论文汇总
        【AI日记】24.11.01 LangChain、openai api和github copilot
      • 热门文章
      • 十款代码表白小特效 一个比一个浪漫 赶紧收藏起来吧!!!
        奉劝各位学弟学妹们,该打造你的技术影响力了!
        五年了,我在 CSDN 的两个一百万。
        Java俄罗斯方块,老程序员花了一个周末,连接中学年代!
        面试官都震惊,你这网络基础可以啊!
        你真的会用百度吗?我不信 — 那些不为人知的搜索引擎语法
        心情不好的时候,用 Python 画棵樱花树送给自己吧
        通宵一晚做出来的一款类似CS的第一人称射击游戏Demo!原来做游戏也不是很难,连憨憨学妹都学会了!
        13 万字 C 语言从入门到精通保姆级教程2021 年版
        10行代码集2000张美女图,Python爬虫120例,再上征途
      Copyright © 2022 侵权请联系2656653265@qq.com    京ICP备2022015340号-1
      正则表达式工具 cron表达式工具 密码生成工具

      京公网安备 11010502049817号