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


自旋
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;}
只有一个查询了数据库
相关文章:
缓存-分布式锁-原理和基本使用
分布式锁原理和使用 自旋 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 redisTe…...
判断国内ip
php代码 //是否国内ip function isChinaIP($ip) {saveLog("---isChinaIP----------");$url "https://searchplugin.csdn.net/api/v1/ip/get?ip".$ip;// 发送HTTP请求$response file_get_contents($url);$utf8String mb_convert_encoding($response, &…...
linux修改内核实现禁止被ping(随手记)
概述 Linux默认允许被ping。其主要决定因素为: 内核参数防火墙(iptables/firewall) 以上的决定因素是与的关系,即需要均满足。 因此,修改linux禁被ping有以上两种方法可以实现。 修改内核文件使禁ping 1. 临时生…...
mac M1安装 VSCode
最近在学黑马程序员Java最新AI若依框架项目开发,里面前端用的是Visual Studio Code 所以我也就下载安装了一下,系统是M1芯片的,安装过程还是有点坑的写下来大家注意一下 1.在appstore中下载 2.在系统终端中输入 clang 显示如下图 那么在终端输…...
代码随想录算法训练营第二十七天 |56. 合并区间 738.单调递增的数字 968.监控二叉树 (可跳过)
56. 合并区间 以数组 intervals 表示若干个区间的集合,其中单个区间为 intervals[i] [starti, endi] 。请你合并所有重叠的区间,并返回 一个不重叠的区间数组,该数组需恰好覆盖输入中的所有区间 。 示例 1: 输入:in…...
网络基础:IS-IS协议
IS-IS(Intermediate System to Intermediate System)是一种链路状态路由协议,最初由 ISO(International Organization for Standardization)为 CLNS(Connectionless Network Service)网络设计。…...
Java面试八股之如何提高MySQL的insert性能
如何提高MySQL的insert性能 提高MySQL的INSERT性能可以通过多种策略实现,以下是一些常见的优化技巧: 批量插入: 而不是逐条插入,可以使用单个INSERT语句插入多行数据。例如: INSERT INTO table_name (col1, col2) V…...
【密码学】什么是密码?什么是密码学?
一、密码的定义 根据《中华人民共和国密码法》对密码的定义如下: 密码是指采用特定变换的方法对信息等进行加密保护、安全认证的技术、产品和服务。 二、密码学的定义 密码学是研究编制密码和破译密码的技术科学。由定义可以知道密码学分为两个主要分支&#x…...
k8s record 20240703
1. containerd 它不用于直接和开发人员互动,在这方面不和docker竞争 containerd的用时最短,性能最好。 containerd 是容器的生命周期管理,容器的网络管理等等,真正让容器运行需要runC containerd 是一个独立的容器运行时&am…...
Ansible常用模块
华子目录 Ansible四个命令模块1.组成2.特点3.区别3.1command、shell模块3.2raw模块 4.command模块4.1参数表4.2free_form参数 5.shell模块5.1作用5.2例如 6.script模块6.1示例 7.raw模块7.1参数7.2示例 文件操作模块1.file模块1.1参数1.2示例 2.copy模块2.1参数 Ansible四个命令…...
【JavaScript脚本宇宙】提升用户体验:探索 JavaScript 库中的浏览器特性支持检测
深入探讨JavaScript库:功能、配置与应用场景 前言 在现代的Web开发中,JavaScript库扮演着至关重要的角色,帮助开发人员简化代码、提高效率、实现更好的用户体验。本文将探讨几个常用的JavaScript库,包括模块加载库、数据绑定库和…...
深度学习:C++和Python如何对大图进行小目标检测
最近在医美和工业两条线来回穿梭,甚是疲倦,一会儿搞搞医美的人像美容,一会儿搞搞工业的检测,最近新接的一个项目,关于瑕疵检测的,目标图像也并不是很大吧,需要放大后,才能看见细小的…...
Eureka从入门到精通面试题及答案参考
什么是Eureka?它在微服务架构中扮演什么角色? Eureka是Netflix开源的一款基于REST的服务发现组件,它主要应用于构建分布式系统中的服务注册与发现。在微服务架构中,Eureka扮演着至关重要的角色,它让微服务架构中的各个服务实例能够互相发现、相互调用,从而实现了服务之间…...
io流 多线程
目录 一、io流 1.什么是io流 2.流的方向 i.输入流 ii.输出流 3.操作文件的类型 i.字节流 1.拷贝 ii.字符流 3.字符流输出流出数据 4.字节流和字符流的使用场景 5.练习 6.缓冲流 1.字节缓冲流拷贝文件 2.字符缓冲流特有的方法 1.方法 2.总结 7.转换流基本用法…...
人工智能、机器学习、神经网络、深度学习和卷积神经网络的概念和关系
人工智能(Artificial Intelligence,缩写为AI)--又称为机器智能,是研究、开发用于模拟、延伸和扩展人的智能的理论、方法、技术及应用系统的一门新的技术科学。 人工智能是智能学科重要的组成部分,它企图了解智能的实质…...
对话大模型Prompt是否需要礼貌点?
大模型相关目录 大模型,包括部署微调prompt/Agent应用开发、知识库增强、数据库增强、知识图谱增强、自然语言处理、多模态等大模型应用开发内容 从0起步,扬帆起航。 基于Dify的QA数据集构建(附代码)Qwen-2-7B和GLM-4-9B&#x…...
【驱动篇】龙芯LS2K0300之ADC驱动
实验目的 由于LS2K0300久久派开发板4.19内核还没有现成可用的ADC驱动,但是龙芯官方的5.10内核已经提供了ADC驱动,想要在4.19内核使用ADC就要参考5.10内核移植驱动,本次实验主要是关于ADC驱动的移植和使用 驱动移植 主要的驱动代码主要有3个…...
Python入门 2024/7/3
目录 for循环的基础语法 遍历字符串 练习:数一数有几个a range语句 三个语法 语法1 语法2 语法3 练习:有几个偶数 变量作用域 for循环的嵌套使用 打印九九乘法表 发工资案例 continue和break语句 函数的基础定义语法 函数声明 函数调用 …...
Go 语言 Map(集合)
Go 语言 Map(集合) Map 是 Go 语言中一种非常重要的数据结构,它用于存储键值对。在 Go 中,Map 是一种无序的键值对的集合,其中每个键都是唯一的,而值则可以是任何类型。Map 是 Go 语言的内置类型,使用起来非常方便,同时也是许多 Go 程序中不可或缺的一部分。 Map 的声明…...
SpringCloud学习Day7:Seata
概念 Seata是一款开源的分布式事务解决方案,致力于在微服务架构下提供高性能和简单易用的分布式事务服务 工作流程 TC以Seata服务器形式独立部署,TM和RM则是以Seata Client的形式集成在微服务中运行...
从怀疑到真香!2026我日常办公离不开的这款在线文字转换器太好用了
刚入职那半年我踩过太多坑:一周三次新人培训,怕漏记知识点全程录音,下课手动整理1小时录音要熬3小时,知识点散得根本没法复习;部门周会做完记录,散会就要我出整理好的纪要,赶工赶得饭都吃不上&a…...
BLE四大广播模式详解:可连接/不可连接/定向/周期广播
一、前言在低功耗蓝牙(BLE)开发中,广播(Advertising)是设备发现、连接建立、数据广播、设备重连的核心基石,所有BLE交互流程均始于广播报文的收发。不同于传统经典蓝牙,BLE所有广播行为标准化、…...
微信小程序3D开发框架技术对比:XR-Frame与threejs-miniprogram
随着微信小程序逐步支持3D渲染与AR能力,开发者面临两个主要官方方案:自研的XR-Frame和适配Three.js的threejs-miniprogram。本文将从架构设计、渲染机制、功能集成、开发模式及适用场景等维度进行技术分析,为技术选型提供参考。一、XR-Frame&…...
sudo企业级应用【20260525】001篇
文章目录 一、总体设计思路 1️⃣ 设计原则 2️⃣ 日志策略(重点) 二、10 个真实生产场景(含 sudoers 配置) 🔹 Linux 系统管理(3 个) ✅ 场景 1:基础运维(用户 / 权限) ✅ 场景 2:磁盘与文件系统 ✅ 场景 3:网络与防火墙 🔹 云管理(2 个) ✅ 场景 4:云 CLI …...
基于ATtiny84的智能冰箱监控器:低功耗温度与门状态监测方案
1. 项目概述:一个装在树莓派盒子里的智能冰箱管家如果你家里有台老冰箱,或者对食物储存温度特别在意,总担心冰箱门没关严或者突然断电导致内部升温,那么这个自己动手做的“冰箱看门狗”项目就太适合你了。它本质上是一个高度定制化…...
AI圈神秘领袖Ilya一幅画引爆全网,OpenAI三件大事暗示AGI时代将至?
AI圈神秘精神领袖Ilya在Instagram上传一幅画引发疯狂解读,与此同时,OpenAI连续公布数学成果、升级Codex、筹备IPO,释放AGI到来的强烈信号。Ilya画作引猜测Ilya上传的画中,罗丹的「思考者」踩在芯片Die Shot上,右下角签…...
PvZ Toolkit终极指南:三步掌握植物大战僵尸最强修改器
PvZ Toolkit终极指南:三步掌握植物大战僵尸最强修改器 【免费下载链接】pvztoolkit 植物大战僵尸 PC 版综合修改器 项目地址: https://gitcode.com/gh_mirrors/pv/pvztoolkit PvZ Toolkit是一款专为植物大战僵尸PC版设计的综合修改器工具,能够让你…...
3分钟告别英文恐惧:Android Studio中文界面轻松切换指南
3分钟告别英文恐惧:Android Studio中文界面轻松切换指南 【免费下载链接】AndroidStudioChineseLanguagePack AndroidStudio中文插件(官方修改版本) 项目地址: https://gitcode.com/gh_mirrors/an/AndroidStudioChineseLanguagePack 你是否曾经因…...
Style-Bert-VITS2未来发展方向:从语音克隆到实时语音转换的技术演进路线
Style-Bert-VITS2未来发展方向:从语音克隆到实时语音转换的技术演进路线 【免费下载链接】Style-Bert-VITS2 Style-Bert-VITS2: Bert-VITS2 with more controllable voice styles. 项目地址: https://gitcode.com/gh_mirrors/st/Style-Bert-VITS2 Style-Bert…...
大厂校招变了:AI 能力正在进入笔试和面试
最近不少同学投递校招时,应该已经发现一个变化: 以前 JD 里写的是“熟悉 Python / Java / SQL / Office 优先”。 现在越来越多岗位开始出现新的描述: “熟练使用 AI 工具者优先” “了解大模型应用者优先” “具备 AI 辅助编程经验优先” “…...
