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


自旋
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的形式集成在微服务中运行...
uniapp 对接腾讯云IM群组成员管理(增删改查)
UniApp 实战:腾讯云IM群组成员管理(增删改查) 一、前言 在社交类App开发中,群组成员管理是核心功能之一。本文将基于UniApp框架,结合腾讯云IM SDK,详细讲解如何实现群组成员的增删改查全流程。 权限校验…...
铭豹扩展坞 USB转网口 突然无法识别解决方法
当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…...
Java 语言特性(面试系列1)
一、面向对象编程 1. 封装(Encapsulation) 定义:将数据(属性)和操作数据的方法绑定在一起,通过访问控制符(private、protected、public)隐藏内部实现细节。示例: public …...
(十)学生端搭建
本次旨在将之前的已完成的部分功能进行拼装到学生端,同时完善学生端的构建。本次工作主要包括: 1.学生端整体界面布局 2.模拟考场与部分个人画像流程的串联 3.整体学生端逻辑 一、学生端 在主界面可以选择自己的用户角色 选择学生则进入学生登录界面…...
【力扣数据库知识手册笔记】索引
索引 索引的优缺点 优点1. 通过创建唯一性索引,可以保证数据库表中每一行数据的唯一性。2. 可以加快数据的检索速度(创建索引的主要原因)。3. 可以加速表和表之间的连接,实现数据的参考完整性。4. 可以在查询过程中,…...
Day131 | 灵神 | 回溯算法 | 子集型 子集
Day131 | 灵神 | 回溯算法 | 子集型 子集 78.子集 78. 子集 - 力扣(LeetCode) 思路: 笔者写过很多次这道题了,不想写题解了,大家看灵神讲解吧 回溯算法套路①子集型回溯【基础算法精讲 14】_哔哩哔哩_bilibili 完…...
vue3 定时器-定义全局方法 vue+ts
1.创建ts文件 路径:src/utils/timer.ts 完整代码: import { onUnmounted } from vuetype TimerCallback (...args: any[]) > voidexport function useGlobalTimer() {const timers: Map<number, NodeJS.Timeout> new Map()// 创建定时器con…...
Spring Boot+Neo4j知识图谱实战:3步搭建智能关系网络!
一、引言 在数据驱动的背景下,知识图谱凭借其高效的信息组织能力,正逐步成为各行业应用的关键技术。本文聚焦 Spring Boot与Neo4j图数据库的技术结合,探讨知识图谱开发的实现细节,帮助读者掌握该技术栈在实际项目中的落地方法。 …...
JS手写代码篇----使用Promise封装AJAX请求
15、使用Promise封装AJAX请求 promise就有reject和resolve了,就不必写成功和失败的回调函数了 const BASEURL ./手写ajax/test.jsonfunction promiseAjax() {return new Promise((resolve, reject) > {const xhr new XMLHttpRequest();xhr.open("get&quo…...
【前端异常】JavaScript错误处理:分析 Uncaught (in promise) error
在前端开发中,JavaScript 异常是不可避免的。随着现代前端应用越来越多地使用异步操作(如 Promise、async/await 等),开发者常常会遇到 Uncaught (in promise) error 错误。这个错误是由于未正确处理 Promise 的拒绝(r…...
