缓存-分布式锁-原理和基本使用

分布式锁原理和使用

 

自旋 

 public Map<String, List<Catelog2Vo>> getCatalogJsonFromDBWithRedisLock() {

        Boolean b = redisTemplate.opsForValue().setIfAbsent(Lock, Lock, Duration.ofMinutes(1));
        if (!b) {
            int i = 10;
            while (i > 0) {
                Object result = redisTemplate.opsForValue().get(CATALOG_JSON);
                try {
                    TimeUnit.MILLISECONDS.sleep(100);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                if (result != null) {
                    System.out.println("命中缓存 db lock");
                    return (Map<String, List<Catelog2Vo>>) result;
                }
                i--;
            }
            throw new RuntimeException("系统繁忙,请重新访问");
        }


        //1.查出所有1级分类
        List<CategoryEntity> selectList = baseMapper.selectList(null);
        /**
         * 将数据库的多次查询变成一次
         */
        System.out.println("查询了数据库");

        //2. 封装数据
        List<CategoryEntity> level1Category = selectList.stream().filter(s -> s.getParentCid().equals(0L)).collect(Collectors.toList());
        Map<String, List<Catelog2Vo>> map = level1Category.stream().collect(Collectors.toMap(k -> k.getCatId().toString(), v -> {
            //1.每一个的一级分类,查到1级分类的所有二级分类
            List<CategoryEntity> categoryEntities = selectList.stream().filter(s -> s.getParentCid().equals(v.getCatId())).collect(Collectors.toList());

            List<Catelog2Vo> catelog2VoList = categoryEntities.stream().map(c -> {
                Catelog2Vo catelog2Vo = new Catelog2Vo();
                catelog2Vo.setId(c.getCatId().toString());
                catelog2Vo.setName(c.getName());
                catelog2Vo.setCatalog1Id(v.getCatId().toString());

                List<CategoryEntity> categoryEntities1 = selectList.stream().filter(s -> s.getParentCid().equals(c.getCatId())).collect(Collectors.toList());
                List<Catelog2Vo.Catelog3Vo> collect = categoryEntities1.stream().map(c3 -> {
                    Catelog2Vo.Catelog3Vo catelog3Vo = new Catelog2Vo.Catelog3Vo();
                    catelog3Vo.setId(c3.getCatId().toString());
                    catelog3Vo.setName(c3.getName());
                    catelog3Vo.setCatalog2Id(c.getCatId().toString());
                    return catelog3Vo;
                }).collect(Collectors.toList());

                catelog2Vo.setCatalog3List(collect);

                return catelog2Vo;
            }).collect(Collectors.toList());


            return catelog2VoList;
        }));
        if (map == null) {
            /**
             * 解决缓存穿透
             */
            map = new HashMap<>();
        }
        redisTemplate.opsForValue().set(CATALOG_JSON, map, Duration.ofDays(1));
        redisTemplate.delete(Lock);
        return map;
    }

 public Map<String, List<Catelog2Vo>> getCatalogJsonFromDBWithRedisLock() {
        String uuid = UUID.randomUUID().toString();
        Boolean b = redisTemplate.opsForValue().setIfAbsent(Lock, uuid, Duration.ofMinutes(1));
        if (!b) {
            int i = 10;
            while (i > 0) {
                Object result = redisTemplate.opsForValue().get(CATALOG_JSON);
                try {
                    TimeUnit.MILLISECONDS.sleep(100);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                if (result != null) {
                    System.out.println("命中缓存 db lock");
                    return (Map<String, List<Catelog2Vo>>) result;
                }
                i--;
            }
            throw new RuntimeException("系统繁忙,请重新访问");
        }


        //1.查出所有1级分类
        List<CategoryEntity> selectList = baseMapper.selectList(null);
        /**
         * 将数据库的多次查询变成一次
         */
        System.out.println("查询了数据库");

        //2. 封装数据
        List<CategoryEntity> level1Category = selectList.stream().filter(s -> s.getParentCid().equals(0L)).collect(Collectors.toList());
        Map<String, List<Catelog2Vo>> map = level1Category.stream().collect(Collectors.toMap(k -> k.getCatId().toString(), v -> {
            //1.每一个的一级分类,查到1级分类的所有二级分类
            List<CategoryEntity> categoryEntities = selectList.stream().filter(s -> s.getParentCid().equals(v.getCatId())).collect(Collectors.toList());

            List<Catelog2Vo> catelog2VoList = categoryEntities.stream().map(c -> {
                Catelog2Vo catelog2Vo = new Catelog2Vo();
                catelog2Vo.setId(c.getCatId().toString());
                catelog2Vo.setName(c.getName());
                catelog2Vo.setCatalog1Id(v.getCatId().toString());

                List<CategoryEntity> categoryEntities1 = selectList.stream().filter(s -> s.getParentCid().equals(c.getCatId())).collect(Collectors.toList());
                List<Catelog2Vo.Catelog3Vo> collect = categoryEntities1.stream().map(c3 -> {
                    Catelog2Vo.Catelog3Vo catelog3Vo = new Catelog2Vo.Catelog3Vo();
                    catelog3Vo.setId(c3.getCatId().toString());
                    catelog3Vo.setName(c3.getName());
                    catelog3Vo.setCatalog2Id(c.getCatId().toString());
                    return catelog3Vo;
                }).collect(Collectors.toList());

                catelog2Vo.setCatalog3List(collect);

                return catelog2Vo;
            }).collect(Collectors.toList());


            return catelog2VoList;
        }));
        if (map == null) {
            /**
             * 解决缓存穿透
             */
            map = new HashMap<>();
        }
        redisTemplate.opsForValue().set(CATALOG_JSON, map, Duration.ofDays(1));
        Object o = redisTemplate.opsForValue().get(Lock);
        if (o != null && o.equals(uuid)) {
            redisTemplate.delete(Lock);
        }

        return map;
    }

还是有问题

因为 在传输过程中需要耗时,这时候如果过期KEY,让其他线程进来创建KEY,然后数据返回到之前那个线程,删除KEY,又会把别人新加进来的key给删掉

获取值对比+对比成功删除=原子操作

 redis+lua脚本实现

 public static final String Lock = "Lock";
  public Map<String, List<Catelog2Vo>> getCatalogJsonFromDBWithRedisLock() {
        String uuid = UUID.randomUUID().toString();
        Boolean b = redisTemplate.opsForValue().setIfAbsent(Lock, uuid, Duration.ofMinutes(5));
        if (!b) {
            System.out.println("获取分布式锁失败,等待重试");
            int i = 10;
            while (i > 0) {
                Object result = redisTemplate.opsForValue().get(CATALOG_JSON);
                try {
                    TimeUnit.MILLISECONDS.sleep(100);
                } catch (InterruptedException e) {
                   e.printStackTrace();
                }
                if (result != null) {
                    System.out.println("命中缓存 db lock");
                    return (Map<String, List<Catelog2Vo>>) result;
                }
                i--;
            }
            throw new RuntimeException("系统繁忙,请重新访问");
        }


        //1.查出所有1级分类
        /**
         * 将数据库的多次查询变成一次
         */
        System.out.println("获取分布式锁成功");

        //2. 封装数据
        Map<String, List<Catelog2Vo>> map = null;
        try {
            System.out.println("查询了数据库");
            List<CategoryEntity> selectList = baseMapper.selectList(null);
            List<CategoryEntity> level1Category = selectList.stream().filter(s -> s.getParentCid().equals(0L)).collect(Collectors.toList());
            map = level1Category.stream().collect(Collectors.toMap(k -> k.getCatId().toString(), v -> {
                //1.每一个的一级分类,查到1级分类的所有二级分类
                List<CategoryEntity> categoryEntities = selectList.stream().filter(s -> s.getParentCid().equals(v.getCatId())).collect(Collectors.toList());

                List<Catelog2Vo> catelog2VoList = categoryEntities.stream().map(c -> {
                    Catelog2Vo catelog2Vo = new Catelog2Vo();
                    catelog2Vo.setId(c.getCatId().toString());
                    catelog2Vo.setName(c.getName());
                    catelog2Vo.setCatalog1Id(v.getCatId().toString());

                    List<CategoryEntity> categoryEntities1 = selectList.stream().filter(s -> s.getParentCid().equals(c.getCatId())).collect(Collectors.toList());
                    List<Catelog2Vo.Catelog3Vo> collect = categoryEntities1.stream().map(c3 -> {
                        Catelog2Vo.Catelog3Vo catelog3Vo = new Catelog2Vo.Catelog3Vo();
                        catelog3Vo.setId(c3.getCatId().toString());
                        catelog3Vo.setName(c3.getName());
                        catelog3Vo.setCatalog2Id(c.getCatId().toString());
                        return catelog3Vo;
                    }).collect(Collectors.toList());

                    catelog2Vo.setCatalog3List(collect);

                    return catelog2Vo;
                }).collect(Collectors.toList());


                return catelog2VoList;
            }));
            if (map == null) {
                /**
                 * 解决缓存穿透
                 */
                map = new HashMap<>();
            }
            redisTemplate.opsForValue().set(CATALOG_JSON, map, Duration.ofDays(1));


        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //lua脚本解锁
            //如果获取key等于传过来的值,就执行删除操作,否则就不执行
            String script="if redis.call('get',KEYS[1])==ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end";
            Long execute = redisTemplate.execute(new DefaultRedisScript<Long>(script, Long.class), Arrays.asList(Lock), uuid);
            if (execute==1){
                System.out.println("原子删锁成功");
            }else {
                System.out.println("原子删锁失败");
            }
        }

        return map;
    }

 只有一个查询了数据库

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【QT】容器类控件

目录 概述 Group Box 核心属性 Tab Widget 核心属性 核心信号 核心方法 使用示例&#xff1a; 布局管理器 垂直布局 核心属性 使用示例&#xff1a; 水平布局 核⼼属性 (和 QVBoxLayout 属性是⼀致的) 网格布局 核心属性 使用示例&#xff1a; 示例&#x…

【python】python猫眼电影数据抓取分析可视化(源码+数据集+论文)【独一无二】

&#x1f449;博__主&#x1f448;&#xff1a;米码收割机 &#x1f449;技__能&#x1f448;&#xff1a;C/Python语言 &#x1f449;公众号&#x1f448;&#xff1a;测试开发自动化【获取源码商业合作】 &#x1f449;荣__誉&#x1f448;&#xff1a;阿里云博客专家博主、5…

安卓虚拟位置修改

随着安卓系统的不断更新&#xff0c;确保软件和应用与最新系统版本的兼容性变得日益重要。本文档旨在指导用户如何在安卓14/15系统上使用特定的功能。 2. 系统兼容性更新 2.1 支持安卓14/15&#xff1a;更新了对安卓14/15版本的支持&#xff0c;确保了软件的兼容性。 2.2 路…

Xilinx FPGA:vivado串口输入输出控制fifo中的数据

一、实验要求 实现同步FIFO回环测试&#xff0c;通过串口产生数据&#xff0c;写入到FIFO内部&#xff0c;当检测到按键信号到来&#xff0c;将FIFO里面的数据依次读出。 二、信号流向图 三、状态转换图 四、程序设计 &#xff08;1&#xff09;按键消抖模块 timescale 1ns…

批量文本编辑管理神器:一键修改多处内容,轻松转换编码,助力工作效率飞跃提升!

在信息爆炸的时代&#xff0c;文本处理已成为我们日常工作中不可或缺的一部分。无论是处理文档、整理数据还是编辑资料&#xff0c;都需要对大量的文本进行管理和修改。然而&#xff0c;传统的文本编辑方式往往效率低下&#xff0c;容易出错&#xff0c;难以满足现代工作的高效…

QListWidget 缩略图IconMode示例

1、实现的效果如下&#xff1a; 2、实现代码 &#xff08;1&#xff09;头文件 #pragma once #include <QtWidgets/QMainWindow> #include "ui_QListViewDemo.h" enum ListDataType { ldtNone -1, ldtOne 0, ldtTwo 1, }; struct ListData…

树莓派4B_OpenCv学习笔记19:OpenCV舵机云台物体追踪

今日继续学习树莓派4B 4G&#xff1a;&#xff08;Raspberry Pi&#xff0c;简称RPi或RasPi&#xff09; 本人所用树莓派4B 装载的系统与版本如下: 版本可用命令 (lsb_release -a) 查询: Opencv 版本是4.5.1&#xff1a; Python 版本3.7.3&#xff1a; ​​ 今日学习&#xff1…

Apache Seata应用侧启动过程剖析——RM TM如何与TC建立连接

本文来自 Apache Seata官方文档&#xff0c;欢迎访问官网&#xff0c;查看更多深度文章。 本文来自 Apache Seata官方文档&#xff0c;欢迎访问官网&#xff0c;查看更多深度文章。 Apache Seata应用侧启动过程剖析——RM & TM如何与TC建立连接 前言 看过官网 README 的第…

Python | Leetcode Python题解之第217题存在重复元素

题目&#xff1a; 题解&#xff1a; class Solution(object):def containsDuplicate(self, nums):if len(set(nums)) ! len(nums):return Trueelse:return False

TCP一定可靠吗

背景 公司某个服务发送TCP报文后,得到的响应是非预期数据 原因竟然是:TCP包的 payload 数据某个bit位被翻转,但是 checksum 的值一样,错误的包被分发给了上层服务 Checksum介绍 IP 头有自己的 Checksum,TCP、UDP 也有自己的 Checksum,分别校验不同部分的数据 IP 头的 …

赛元单片机开发工具SOC_Programming_Tool_Enhance_V1.50 分享

下载地址&#xff1a; SOC_Programming_Tool_Enhance_V1.50(LIB0D30).rar: https://545c.com/f/45573183-1320016694-557ebd?p7526 (访问密码: 7526)

使用Spring Boot和自定义缓存注解优化应用性能

在现代应用开发中&#xff0c;缓存是提高系统性能和响应速度的关键技术之一。Spring Boot提供了强大的缓存支持&#xff0c;但有时我们需要更灵活的缓存控制。本文将介绍如何使用Spring Boot和自定义缓存注解来优化应用性能。 1. 为什么需要自定义缓存注解&#xff1f; Sprin…

干货 | 2024大模型场景下智算平台的设计与优化实践(免费下载)

诚挚邀请您微信扫描以下二维码加入方案驿站知识星球&#xff0c;获取上万份PPT/WORD解决方案&#xff01;&#xff01;&#xff01;感谢支持&#xff01;&#xff01;&#xff01;

在linux系统centos上面安装php7gmp扩展

ps:在ubuntu上面安装gmp(最简单) $ sudo apt-get install php7.0-gmp然后再php.ini添加extensionphp_gmp.so <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<…

Vue3中生成本地pdf并下载

1. 前言 前端中经常会遇到在系统中根据数据导出一个pdf文件出来,一般都是后端来实现的,既然后端可以实现,前端为什么就不行呢,正好有一次也写了这个需求,就写了个小demo 示例图: 2. 实现步骤 首先下载html2pdf.js这个库yarn add html2pdf.js // 或 npm i html2pdf.js在项…

欧洲杯数据控@20240706

点击标题下「蓝色微信名」可快速关注 上半区西班牙、法国脱颖而出&#xff0c;将会争夺一个决赛的席位&#xff0c;下半区两场比赛&#xff0c;将会决出另外两支进入半决赛的球队&#xff0c; 今日射手榜&#xff0c;随着球队的淘汰&#xff0c;能争夺金靴的球员越来越少了&…

17.优化算法之解决拓扑排序4

0.基础 1.课程表1 207. 课程表 - 力扣&#xff08;LeetCode&#xff09; class Solution {public boolean canFinish(int n, int[][] p) {// 1. 准备⼯作int[] in new int[n]; // 统计每⼀个顶点的⼊度Map<Integer, List<Integer>> edges new HashMap<>…

整洁架构SOLID-开闭原则(OCP)

文章目录 1 定义2 最佳实践2.1 需求2.2 需求变更2.3 变更原则2.4 实现逻辑2.4.1 组件化2.4.2 组件关系 2.5 依赖方向的控制 3 本章小结 1 定义 开闭原则(OCP)是Bertrand Meyer在1988年提出的&#xff0c;该设计原则认为&#xff1a; 设计良好的计算机软件应该易于扩展&#xf…

认识并理解webSocket

今天逛牛客&#xff0c;看到有大佬分享说前端面试的时候遇到了关于webSocket的问题&#xff0c;一看自己都没见过这个知识点&#xff0c;赶紧学习一下&#xff0c;在此记录&#xff01; WebSocket 是一种网络通信协议&#xff0c;提供了全双工通信渠道&#xff0c;即客户端和服…

Unity3D游戏 RPG

丛林探险游戏 人物进行探险游戏 拥有登录&#xff0c;首页&#xff0c;3D物体旋转浏览的功能&#xff0c;还能进行种植树等功能