3.redis-事务

news2024/12/24 22:10:09

01-Redis事务概述

  • 概述
    • Redis事务是一个单独的隔离操作:事务中的所有命令都会序列化放到一个队列中按顺序地执行。事务 在执行的过程中,不会被其他客户端发送来的命令请求所打断。
  • 不支持ACID
    • ①atomicity, 原子性, redis事务中的指令执行失败, 不影响后续指令执行;
    • ②durability, 持久性, redis数据存储在内存中, 可以使用AOF和RDB进行补充.

02-Redis事务指令

  • 语法

    #开启事务, 后续指令放入到事务队列中
    multi
    
    #提交事务, 执行事务队列中的指令, 结束事务
    exec
    
    #回滚事务, 清空事务队列中的指令,结束事务
    discard
    
  • 代码实现1: 成功

    • gah3u4hw36t5eq23t4egy6a
  • 注意事项

    • exec指令执行之后可能会有多个结果, 要使用集合来封装.

03-Redis事务错误处理

  • ①编译期错误: 语法错误, 后续指令不能执行

    • a4t54huywa45tygaw
    • image-20230303142444189
  • ②运行时错误: 语法正确, 执行错误. 不影响后续指令执行.

    • hba3ehujb2w4huqa23huyq32

    • gha236i9aq2trgyhaw

04-Redis锁机制

  • 事务冲突问题

    • awe43uha354yghse

    • 张三账户余额有10000元,他想在双11的时候抢购三款手机:mate20pro(价值1000元)、 mate30pro(价值5000元)、mate40pro(价值8000元),为了增加抢购成功率,他把账户给了另外两个 好朋友:李四、王五,让他们在双11帮忙抢购。结果,双11,他们三个人同时分别抢到了 mate20pro、mate30pro、mate40pro,然后他们都同时付了款…

  • 悲观锁

    • gha3wy3whauh487ui

    • 效率低

  • 乐观锁

    • ygq3ah523e4rht567j2
  • 总结

    • Redis采用CAS乐观锁.

05-Redis乐观锁

  • 代码实现
    • 2q3467yaqgyh34TYG3QA32

06-秒杀案例需求分析及准备

  • 需求

    • 秒杀同一件商品(一件商品多个库存)
  • 分析

    • afgw3gyh3wq1q2ty36

    • ①商品库存

      • string类型, key=seckill:商品id:kc
    • ②秒杀成功者清单

      • set类型, key=seckill:商品id:usr
  • 准备

    set seckill:0101:kc 100
    

07-秒杀案例基本功能

  • 开发步骤

    • ①编写前端代码
    • ②编写表现层代码
    • ③编写SeckillUtils工具类
      • 执行秒杀
  • ①编写前端代码

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>首页</title>
        <script src="script/vue.js"></script>
        <script src="script/axios.js"></script>
    </head>
    <body>
    <div id="app">
        <button @click="handleSeckill">开始秒杀</button>
    </div>
    <script>
        Object.assign(window, Vue);
        const App = {
            setup() {
                const data = reactive({
                    cid: "1001"
                })
                const handleSeckill = () => {
                    axios({
                        method:"post",
                        url:"/doSeckill",
                        params:{
                            cid:data.cid
                        }
                    }).then((res) => {
                        const {msg}=res.data;
                        alert(msg);
                        console.log(res.data);
                    })
                }
                return {
                    data,
                    handleSeckill
                }
            }
        }
        Vue.createApp(App).mount('#app');
    </script>
    </body>
    </html>
    
  • ②编写表现层代码

    @Controller
    public class SeckillController {
        @PostMapping("/doSeckill/{cid}")
        @ResponseBody
        public ResultVO<Object> doSeckill(@PathVariable String cid){
            //执行秒杀
            String uid = UUID.randomUUID().toString().replace("-", "");
            ResultVO<Object> result= SeckillUtils.doSeckill(uid,cid);
            System.out.println("result = " + result);
            return result;
        }
    }
    
  • 编写jedis.properties配置问价

    # redis主机地址
    jedis.host=192.168.199.110
    # redis端口
    jedis.port=6379
    # 最大连接数
    jedis.maxTotal=1000
    # 控制一个pool最多有多少个状态为空闲的jedis实例
    jedis.maxIdle=100
    # 最大的等待毫秒数,如果超过等待时间,则直接抛JedisConnectionException
    jedis.maxWaitMillis=10000
    
  • ③编写ResultVo.java工具类

    @NoArgsConstructor
    @AllArgsConstructor
    @Data
    public class ResultVO<T> {
        private boolean flag;
        private String msg;
        private T data;
    }
    
  • ④编写JedisUtils工具类

    public class JedisUtils {
            private static JedisPool jedisPool;
    
            static {
                InputStream inputStream = JedisUtils.class.getClassLoader().getResourceAsStream("jedis.properties");
                Properties properties = new Properties();
                try {
                    properties.load(inputStream);
                    int maxTotal = Integer.valueOf(properties.getProperty("jedis.maxTotal"));
                    int maxIdle = Integer.valueOf(properties.getProperty("jedis.maxIdle"));
                    long maxWaitMillis = Integer.valueOf(properties.getProperty("jedis.maxWaitMillis"));
                    String host = properties.getProperty("jedis.host");
                    int port = Integer.valueOf(properties.getProperty("jedis.port"));
    
                    //创建Jedis链接池对象
                    JedisPoolConfig poolConfig = new JedisPoolConfig();
                    poolConfig.setMaxTotal(maxTotal);
                    poolConfig.setMaxIdle(maxIdle);
                    poolConfig.setMaxWaitMillis(maxWaitMillis);
                    jedisPool = new JedisPool(
                            poolConfig,
                            host,
                            port
                    );
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
    
            public static Jedis getJedis() {
                return jedisPool.getResource();
            }
    
            public static void close(Jedis jedis) {
                if ( null != jedis) {
                    jedis.close();
                    jedis = null;
                }
            }
    }
    
  • ③编写SeckillUtils工具类

    public class SeckillUtils {
        /**
         * 执行秒杀
         * @param uid 用户id
         * @param cid 商品id
         * @return
         */
        public static ResultVO<Object> doSeckill(String uid, String cid) {
            //1.生成库存key [string:存储简单类型] [set seckill:1001:kc 100]
            String kcKey="seckill:"+cid+":kc";
            System.out.println("kcKey = " + kcKey);
            //2.生成秒杀成功者清单key [set:存储多个结果且去重] [sadd seckill:1001:user user1 user2...]
            String userKey="seckill:"+cid+":user";
    
            //3.判断秒杀是否开始
            Jedis jedis = JedisUtils.getJedis();
            String kcStr = jedis.get(kcKey);
            System.out.println("kcStr = " + kcStr);
            if(kcStr==null){
                JedisUtils.close(jedis);
                return  new ResultVO<Object>(
                        false,
                        "秒杀未开始,敬请期待!",
                        null
                );
            }
    
            //4.判断秒杀是否结束
            int kc = Integer.parseInt(kcStr);
            if(kc<=0){
                JedisUtils.close(jedis);
                return  new ResultVO<Object>(
                        false,
                        "秒杀已结束!",
                        null
                );
            }
    
            //5.判断当前用户是否参加过秒杀
            if (jedis.sismember(userKey,uid)) {
                JedisUtils.close(jedis);
                return  new ResultVO<Object>(
                        false,
                        "不能重复参加秒杀活动!",
                        null
                );
            }
    
            //6.开始秒杀
            //库存减一
            jedis.decr(kcKey);
            //记录秒杀成功的用户
            jedis.sadd(userKey,uid);
            JedisUtils.close(jedis);
            return  new ResultVO<Object>(
                    false,
                    "秒杀成功!",
                    null
            );
        }
    }
    

08-ab工具模拟秒杀高并发

  • 超卖问题

    • yhw4uyuw4qahu4whbu4w56

    • 有多个用户同时执行了秒杀操作,且都认为当前有库存,都执行了库存减一,所以就出现了超卖问 题。

    • 使用ab插件模拟高并发.

  • 开发步骤

    • ①安装ab插件
      • yum -y install httpd-tools
    • ②创建请求参数文件
      • /usr/local/data.txt
    • ③开始模拟高并发
  • ①安装ab插件

  • ②创建请求参数文件

    cid=1001&
    
  • ③开始模拟高并发

    ab -n 1000 -c 200 -p /usr/local/data.txt -T "application/x-www-form-urlencoded" 192.168.13.54:8080/doSeckill
    

09-秒杀案例解决超卖问题

  • 分析

    • uhw4s6ujiw46s5
    • 利用乐观锁淘汰用户,解决超卖问题。
  • 代码实现

    /**
     * 解决超卖问题
     */
    public class SeckillUtils1 {
        /**
         * 执行秒杀
         * @param uid 用户id
         * @param cid 商品id
         * @return
         */
        public static ResultVO<Object> doSeckill(String uid, String cid) {
            //1.生成库存key [string:存储简单类型] [set seckill:1001:kc 100]
            String kcKey="seckill:"+cid+":kc";
            System.out.println("kcKey = " + kcKey);
            //2.生成秒杀成功者清单key [set:存储多个结果且去重] [sadd seckill:1001:user user1 user2...]
            String userKey="seckill:"+cid+":user";
    
            //3.判断秒杀是否开始
            Jedis jedis = JedisUtils.getJedis();
            //4.给库存key加上乐观锁
            jedis.watch(kcKey);
            String kcStr = jedis.get(kcKey);
            System.out.println("kcStr = " + kcStr);
            if(kcStr==null){
                JedisUtils.close(jedis);
                return  new ResultVO<Object>(
                        false,
                        "秒杀未开始,敬请期待!",
                        null
                );
            }
    
            //4.判断秒杀是否结束
            int kc = Integer.parseInt(kcStr);
            if(kc<=0){
                JedisUtils.close(jedis);
                return  new ResultVO<Object>(
                        false,
                        "秒杀已结束!",
                        null
                );
            }
    
            //5.判断当前用户是否参加过秒杀
            if (jedis.sismember(userKey,uid)) {
                JedisUtils.close(jedis);
                return  new ResultVO<Object>(
                        false,
                        "不能重复参加秒杀活动!",
                        null
                );
            }
    
            //6.开始秒杀
            //开启事务
            Transaction multi = jedis.multi();
            //库存减一
            multi.decr(kcKey);
            //记录秒杀成功的用户
            multi.sadd(userKey,uid);
            //提交事务
            List<Object> result = multi.exec();
            //判断事务是否执行失败
            if ( result==null ||  result.size()==0) {
                JedisUtils.close(jedis);
                return  new ResultVO<Object>(
                        false,
                        "商品已被抢走!",
                        null
                );
            }
            JedisUtils.close(jedis);
            return  new ResultVO<Object>(
                    false,
                    "秒杀成功!",
                    null
            );
        }
    }
    

10-秒杀案例解决库存遗留问题

  • 概述

    • “ab -n 1000”,ab模拟高并发,意味着有1000个用户在参与秒杀,如果库存1000的话,商品绝对是 可以秒杀完的;
    • “ab -n 1000 -c 200”,重新解读下ab模拟高并发,1000次请求,最高会有200个请求是并发的,意味 着会有200个请求抢同一件商品,如果是这种情况,就会存在明明有1000个人抢,但是只被抢走5个商 品,就会出现库存遗留问题。
  • 概述

    • Lua是一个小巧的脚本语言,Lua脚本可以很容易的被C/C++ 代码调用,也可以反过来调用C/C++的 函数,Lua并没有提供强大的库,一个完整的Lua解释器不过200k,所以Lua不适合作为开发独立应用 程序的语言,而是作为嵌入式脚本语言。
    • 将复杂的或者多步的redis操作,写为一个脚本,一次提交给redis执行,减少反复连接redis的次数。 提升性能。
    • LUA脚本是类似redis事务,有一定的原子性,不会被其他命令插队,可以替代redis事务完成一些的操 作。
  • 开发步骤

    • ①修改SeckillUtils工具类
      • 引入LUA脚本
    • ②代码测试
  • ①修改SeckillUtils工具类

    /**
     * LUA脚本解决库存遗留问题
     */
    public class SeckillUtils {
        //将之前的多次请求,使用LUA脚本拼接成一次请求
        static String secKillScript = "local uid=KEYS[1];\r\n" +
                "local cid=KEYS[2];\r\n" +
                "local qtkey='seckill:'..cid..\":kc\";\r\n" +
                "local usersKey='seckill:'..cid..\":usr\";\r\n" +
                "local userExists=redis.call(\"sismember\",usersKey,uid);\r\n" +
                "if tonumber(userExists)==1 then \r\n" +
                "   return 2;\r\n" +
                "end\r\n" +
                "local num= redis.call(\"get\" ,qtkey);\r\n" +
                "if tonumber(num)<=0 then \r\n" +
                "   return 0;\r\n" +
                "else \r\n" +
                "   redis.call(\"decr\",qtkey);\r\n" +
                "   redis.call(\"sadd\",usersKey,uid);\r\n" +
                "end\r\n" +
                "return 1";
    
        public static ResultVO<Object> doSeckill(String uid, String prodid) throws IOException {
            Jedis jedis = JedisUtils.getJedis();
            String sha1 = jedis.scriptLoad(secKillScript);
            Object result = jedis.evalsha(sha1, 2, uid, prodid);
            String reString = String.valueOf(result);
            if ("0".equals(reString)) {
                System.out.println("失败!秒杀已经结束!");
                JedisUtils.close(jedis);
                return new ResultVO<Object>(false, "秒杀已经结束!", null);
            } else if ("1".equals(reString)) {
                System.out.println("抢购成功!!!!");
            } else if ("2".equals(reString)) {
                System.out.println("失败!您已经秒杀成功过了!把机会给别人吧!");
                JedisUtils.close(jedis);
                return new ResultVO<Object>(false, "不能重复秒杀!", null);
            } else {
                System.err.println("抢购异常!!");
                JedisUtils.close(jedis);
                return new ResultVO<Object>(false, "抢购异常!", null);
            }
            JedisUtils.close(jedis);
            return new ResultVO<Object>(true, "秒杀成功!", null);
    
        }
    }
    

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/439345.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

记一次 腾讯会议 的意外崩溃分析

一&#xff1a;背景 1. 讲故事 前段时间在用 腾讯会议 直播的时候&#xff0c;居然意外崩溃了&#xff0c;还好不是在训练营上课&#xff0c;不然又得重录了&#xff0c;崩完之后发现 腾讯会议 的 bugreport 组件会自动生成一个 minidump&#xff0c;截图如下&#xff1a; 作…

tcpdump 抓包

一、Tcpdump抓包 抓取端口为2008的数据包 抓包文件内容 抓取到的内容保存在文件中&#xff0c;可以通过wireshark分析 二、tcpdump的一些命令 tcpdump和ethereal可以用来获取和分析网络通讯活动&#xff0c;他们都是使用libpcap库来捕获网络封包的。 ​在混杂模式下他们可以监…

Oracle-主备切换问题(BUG-31747989)

背景: 用户在Oracle Rac 19.6版本通过switchover方式进行主备切换&#xff0c;在备切主完成之后&#xff0c;进行open的过程中&#xff0c;状态长时间无法完成疑似hang住。 问题: ​ Oracle Rac 19.6版本通过switchover方式进行主备切换,切换完成之后进行open&#xf…

100个开源手写人工智能算法(持续更新中)

你是否曾经对开源框架中算法的运行原理感到好奇&#xff1f;又是否曾经想过使用自己写的算法来进行机器学习&#xff1f; 现在&#xff0c;向您推荐一款基于 Python 语言的开源手写机器学习算法项目&#xff01; https://github.com/yuluxingchen/AI/ 这个项目中预计将包含了…

【10 浅学jsp】

浅学 jsp 一、jsp1. jsp介绍2. JSP执行流程3. JSP的本质还是Servlet4. JSP语法4.1 注释4.2 代码块4.3JSP表达式4.4JSP声明 5. JSP指令page 指令include 包含指令包含 其他页面taglib 引入外部标签库 6. JSP细节6.1 pageContext 页面域对象 7. Servlet四大域对象小结8. MVC模型 …

利用MFC实现一个托盘功能

文章目录 1.将主窗口隐藏起来&#xff0c;并移除任务栏图标显示2. 制作系统托盘3. 右键托盘得到信息4. 选择信息栏触发事件5. 添加开机自启动与关闭开机自启动OnSetPowerBoot与OnCancelPowerBoot 右键点击托盘&#xff0c;弹出如下的图标 1.将主窗口隐藏起来&#xff0c;并移除…

Qt开发技术:Q3D图表开发笔记(三):Q3DSurface三维曲面图介绍、Demo以及代码详解

若该文为原创文章&#xff0c;转载请注明原文出处 本文章博客地址&#xff1a;https://hpzwl.blog.csdn.net/article/details/130264470 各位读者&#xff0c;知识无穷而人力有穷&#xff0c;要么改需求&#xff0c;要么找专业人士&#xff0c;要么自己研究 红胖子网络科技博…

怎么批量把heic格式转化jpg,3招快速解决

怎么批量把heic格式转化jpg&#xff1f;heic是一种新型的图像文件格式&#xff0c;是苹果独家搞出来的一个图片格式&#xff0c;它小巧玲珑&#xff0c;而且图像质量超好&#xff0c;专门给iOS11系统用户用的。这种格式比老JPEG更厉害&#xff0c;不仅图片质量好&#xff0c;而…

如何减少电脑内存占用或优化内存?

内存(Memory)是计算机一个重要的组成部件&#xff0c;也称为内存储器或主存储器。它可以暂时存放CPU中运算的数据&#xff0c;以及与硬盘等外部存储器交换的数据&#xff0c;是CPU和硬盘之间的桥梁。若电脑内存占用过高&#xff0c;这会影响到电脑运行的速度&#xff0c;那该如…

Redis的哈希槽分区

目录 1. 一致性算法分区的缺点2. 哈希槽分区3. Redis为什么是16384个槽 1. 一致性算法分区的缺点 可以参考一致性哈希算法分区这篇文章 2. 哈希槽分区 Redis集群中内置了16384个哈希槽。redis会根据服务器节点数量大致均等的将哈希槽映射到不同的节点 当写入一条数据&#x…

Java版本的工程项目管理系统源代码之工程项目管理系统面临的挑战

​ ​工程项目管理系统是指从事工程项目管理的企业&#xff08;以下简称工程项目管理企业&#xff09;受业主委托&#xff0c;按照合同约定&#xff0c;代表业主对工程项目的组织实施进行全过程或若干阶段的管理和服务。 ​系统定义 工程项目管理企业不直接与该工程项目的总承包…

UE4/5多人游戏详解(六、多人游戏插件的菜单,创建会话设置和加入)

目录 简单的菜单 创建新的c类&#xff1a; 这里可能出现的报错&#xff1a; 菜单设置&#xff1a; 代码&#xff1a; UI创建&#xff1a; C类中创建按钮的指针&#xff1a; 子系统创建 创建会话函数&#xff1a; 创建会话后前往大厅&#xff1a; 重载函数 变量添加…

Java 线程

线程&#xff1a;线程是进程的组成部分&#xff0c;一个进程可以拥有多个线程&#xff0c;而一个线程必须拥有一个父进程。线程可以拥有自己的堆栈&#xff0c;自己的程序计数器和自己的局部变量&#xff0c;但不能拥有系统资源。它与父进程的其他线程共享该进程的所有资源。 …

PowerShell install Docker+docker-compoer

docker 前言 Docker 是一个开源的应用容器引擎&#xff0c;让开发者可以打包他们的应用以及依赖包到一个可移植的镜像中&#xff0c;然后发布到任何流行的 Linux或Windows 机器上&#xff0c;也可以实现虚拟化。容器是完全使用沙箱机制&#xff0c;相互之间不会有任何接口。 …

Linux搭建SVN服务器详细教程

前言 本文讲解 Linux 系统下如何搭建 SVN 服务器&#xff0c;详细说明各配置项的功能&#xff0c;最终实现可管控多个项目的复杂配置。 SVN 是 subversion 的缩写&#xff0c;是一个开放源代码的版本控制系统&#xff0c;通过采用分支管理系统的高效管理&#xff0c;实现最终集…

【转】使用Midjourney绘制小漫画

原帖地址:【Midjourney教程】设计麻瓜也能15分钟一篇小漫画 Midjourney能帮我画漫画,话不多说,下方成品图 Part 1 你想画什么 画漫画当然要有故事情节,你总得确定,你要画个啥?也就是专业人士说的画面分镜,当然咱们是“野狐禅”,就不扯的太细,太细我也不会… 由于只…

软考中级软件评测师备考攻略

软件评测师属于软考中级&#xff0c;考试虽然没有软考高级难度大&#xff0c;但是会比软考初级要难&#xff0c;所以想要通过软件评测师考试还是需要花时间去用心备考的。 一、软件评测师职业前景&#xff1a; 随着互联网技术的不断发展&#xff0c;软件评测师的市场需求也会…

【CocosCreator入门】CocosCreator组件 | Canvas(画布)组件

Cocos Creator 是一款流行的游戏开发引擎&#xff0c;具有丰富的组件和工具&#xff0c;其中的Canvas能够将游戏物体渲染到屏幕上。 目录 一、组件介绍 二、渲染模式 三、组件属性 四、组件使用 五、脚本示例 一、组件介绍 Canvas组件是Cocos Creator中重要的组件之一。在…

【Socket】之TCP数据报套接字

1. 介绍下API 1.1 ServerSocket API 这是创建TCP服务端Socket的API。 构造方法方法说明ServerSocket(int port)创建一个服务端流套接字Socket&#xff0c;并绑定到指定端口 普通方法方法说明ServerSocket.accept()开始监听指定端口&#xff08;创建时绑定的端口&#xff09…

Spring学习——Nginx

Nginx概述 Nginx介绍 Nginx是一款轻量级的web 服务器/反向代理服务器及电子邮件&#xff08;IMAP/POP3&#xff09;代理服务器。其特点是占有内存少&#xff0c;并发能力强&#xff0c;事实上nginx的并发能力在同类型的网页服务器中表现较好&#xff0c;中国大陆使用nginx的网…