首页/新闻资讯/正文详情

java - redis 缓存击穿 -逻辑过期

发布时间:2026/11/6 1:36:24 来源:云帆数科 栏目:资讯中心
java - redis 缓存击穿 -逻辑过期
逻辑过期 代码使用 逻辑过期 解决 缓存击穿逻辑过期时长这里设20秒privatestaticfinallongLOGIC_EXPIRE_SECONDS20L;创建独立线程池专门用来异步更新缓存只创建一次privatestaticfinalExecutorServiceCACHE_REBUILD_EXECUTORExecutors.newFixedThreadPool(10);OverridepublicResultqueryShopByLogicExpire(Longid){StringkeyCACHE_SHOP_KEYid;StringshopJsonstringRedisTemplate.opsForValue().get(key);// 逻辑过期方案需要提前预热缓存如果redis没有key直接返回不存在if(StrUtil.isBlank(shopJson)){returnResult.fail(商铺不存在);}// 反序列化RedisData对象RedisDataredisDataJSONUtil.toBean(shopJson,RedisData.class);Shopshop(Shop)redisData.getData();LocalDateTimeexpireTimeredisData.getExpireTime();判断 逻辑过期 是否过期过期时间在当前时间之后 未过期if(expireTime.isAfter(LocalDateTime.now())){// 未过期直接返回商铺信息returnResult.ok(shop);}走到这里缓存逻辑过期了StringlockKeyLOCK_SHOP_KEYid;booleanisLocktryLogicLock(lockKey);if(isLock){// 获取锁成功开启异步线程去更新缓存CACHE_REBUILD_EXECUTOR.submit(()-{try{saveShopRedis(id,LOGIC_EXPIRE_SECONDS);}finally{// 释放锁unlock(lockKey);}});}重点不管拿到锁还是没拿到锁主线程直接返回旧数据不等待returnResult.ok(shop);}// 逻辑过期时间 - 获取锁privatebooleantryLogicLock(StringlockKey){// NX锁10秒BooleanflagstringRedisTemplate.opsForValue().setIfAbsent(lockKey,1,10,TimeUnit.SECONDS);returnBooleanUtil.isTrue(flag);}缓存预热方法项目启动时手动调用提前把数据放入Redis。逻辑过期方案必须先预热不能等查询的时候才存入redis。publicvoidsaveShopRedis(Longid,LongexpireSeconds){// 1. 查询数据库ShopshopgetById(id);// 2. 封装RedisDataRedisDataredisDatanewRedisData();redisData.setData(shop);redisData.setExpireTime(LocalDateTime.now().plusSeconds(expireSeconds));// 3. 写入Redis不设置过期时间stringRedisTemplate.opsForValue().set(CACHE_SHOP_KEYid,JSONUtil.toJsonStr(redisData));}privatevoidunlock(StringlockKey){stringRedisTemplate.delete(lockKey);}完整执行流程前提提前调用 saveShopRedis() 预热cache:shop:1已经存在Rediskey没有物理过期时间value里面包含shop数据 逻辑过期时间。用户请求接口传入商铺 id拼接 cache:shop:1查询 Redis 拿到 json 字符串。判断StrUtil.isBlankRedis有数据继续执行。JSON字符串反序列成RedisData对象。取出shop对象强制转换Object→Shop、取出逻辑过期时间expireTime。判断 expireTime 是否在当前时间之后✅未过期直接返回shop请求结束。❌已过期进入缓存更新逻辑。拼接锁 keylock:shop:1尝试获取分布式锁。✅抢到锁向线程池提交异步任务新开线程执行saveShopRedis。异步线程查DB封装RedisData写入Redis更新数据与新的逻辑过期时间最后释放锁。❌没抢到锁不做任何操作。主线程不等待异步任务直接返回旧的shop数据给前端后续再来查询这个商铺Redis已经是异步线程更新后的新数据逻辑过期时间也刷新直接返回新数据。核心特点缺陷✅特点Redis 的 key 物理上永不过期靠 value 内部的 expireTime 判断过期缓存过期后用户请求不会阻塞没有sleep高并发性能好分布式锁只用来控制同一时刻最多只有1个异步线程更新缓存不是阻塞用户弱一致性缓存过期之后异步更新完成前所有用户拿到旧数据。❌缺陷必须提前预热缓存不能访问时动态加载忘记预热直接返回不存在弱一致性存在旧数据窗口解锁直接delete没有锁持有者校验存在锁误删风险Redis key永久存在占用Redis内存不能解决缓存穿透只解决缓存击穿。和互斥锁方案一句话区分互斥锁Redis 真实删除 key请求阻塞等待读到最新数据强一致性。逻辑过期Redis 保留 key判断内部逻辑时间请求不阻塞短暂读到旧数据高并发。Executors.newFixedThreadPool(10)privatestaticfinalExecutorServiceCACHE_REBUILD_EXECUTORExecutors.newFixedThreadPool(10);ExecutorService是线程池接口定义提交任务、关闭线程池等方法。Executors.newFixedThreadPool(10)返回的对象实现了这个接口用接口接收面向接口编程CACHE_REBUILD_EXECUTOR变量名语义缓存重建线程池。专门用来跑「异步更新缓存」的任务不和Tomcat 的 HTTP工作线程 混用。Executors.newFixedThreadPool(10)Executors是 JDK 提供的 线程池工具类。newFixedThreadPool(10)→固定大小线程池核心线程数量10。特性线程数量固定为10个最多同时跑10个缓存重建任务任务多了多余任务进入无界队列LinkedBlockingQueue排队等待线程不会自动回收常驻内存适合短时间、高频的异步任务这里就是用来后台更新Redis缓存CACHE_REBUILD_EXECUTOR.submit(()-{try{saveShopRedis(id,LOGIC_EXPIRE_SECONDS);}finally{unlock(lockKey);}});.submit()提交任务给线程池从线程池拿出一条空闲线程去执行括号里面的代码而当前用户请求的主线程Tomcat线程不会卡住直接往下执行return返回旧数据核心目的把缓存重建任务交给独立线程池异步执行不占用Tomcat处理HTTP请求的工作线程。如果不单独开线程池用 Tomcat 本身的线程去做更新那这个请求就会被阻塞逻辑过期 “不阻塞” 的优势就没了。无界队列固定线程池newFixedThreadPool(10)最多同时10个线程干活。无界队列里排队的任务只要有一个线程空闲出来就从队列头部取出一个任务交给这个线程执行。例子线程池核心线程 10第110个任务直接分配给10条线程全部开始执行第11、12、13…任务线程全部忙进LinkedBlockingQueue无界队列排队假设任务1执行完毕线程1空闲了线程池检测到有空闲线程从队列队首取出排在最前面的任务任务11线程1执行任务11队列里剩下任务继续等待哪个线程干完活就从队列拿一个新任务队列是先进先出 FIFO先来的任务先被执行。⚠️ 重点LinkedBlockingQueue 是无界队列队列没有上限可以无限往里塞任务。极端场景大量缓存同时过期疯狂submit任务任务不断堆在队列里队列越来越大占用堆内存最终OOM。这就是阿里开发手册禁止直接用Executors.newFixedThreadPool的原因。补充线程池 和 Tomcat线程 区别Tomcat线程池专门接收、处理用户 http 请求请求来了就用这个线程执行 controller/service 代码。CACHE_REBUILD_EXECUTOR独立线程池只做后台缓存更新和http请求线程隔离。用户主线程Tomcat线程只是提交任务提交完就解放直接返回结果真正查DB、更新Redis的动作交给缓存重建线程池里的线程。面试小问题Q为什么要单独创建一个线程池不用Tomcat线程直接做更新A如果用 Tomcat 线程去同步更新缓存主线程就会阻塞等待更新完成逻辑过期方案“不阻塞请求”的特点就失效。单独线程池把重建任务异步后台执行Tomcat 请求线程提交任务后立刻返回提升并发能力。Qstatic在这里的意义Astatic 保证整个项目只有这一个线程池实例不会多次创建多个线程池避免创建大量多余线程消耗系统资源。Qfinal的作用A防止代码其他地方不小心把CACHE_REBUILD_EXECUTOR重新赋值为null或者另一个线程池保证引用不变。QExecutors.newFixedThreadPool()底层队列是无界队列。如果短时间大量缓存同时过期大量任务不断丢进队列队列无限膨胀会OOM内存溢出。面试标准答案开发演示写这个没问题生产环境不推荐 Executors 快捷创建线程池推荐手动ThreadPoolExecutor构造方法自己指定队列最大容量 拒绝策略防止任务堆积OOM。

相关推荐

claudecode,deepseek,codex上下文压缩方式
claudecode,deepseek,codex上下文压缩方式

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/6 1:36:24

5 分钟把硬盘写进群晖 DSM 兼容数据库:第三方硬盘“不受支持“警告清除指南
5 分钟把硬盘写进群晖 DSM 兼容数据库:第三方硬盘“不受支持“警告清除指南

5 分钟把硬盘写进群晖 DSM 兼容数据库:第三方硬盘"不受支持"警告清除指南 【免费下载链接】Synology_HDD_db Add your HDD, SSD and NVMe drives to your Synologys compatible drive database and a lot more 项目地址: https://gitcode.com/GitHub_Tr… · 2026/11/6 1:36:24

MySQL约束条件一览
MySQL约束条件一览

文章目录1 前言2 空属性 null/not null3 默认值 default4 列描述 comment5 零填充 zerofill6 主键 primary key7 自增长 auto_increment8 唯一键 unique9 外键 foreign key1 前言 约束:数据库通过技术手段限制用户插入的数据,保证数据合法、正确、完整、… · 2026/11/6 1:36:24

Folia 歌词动画播放器 4 个关卡零基础上手:第一次播通你的第一首歌
Folia 歌词动画播放器 4 个关卡零基础上手:第一次播通你的第一首歌

Folia 歌词动画播放器 4 个关卡零基础上手:第一次播通你的第一首歌 【免费下载链接】folia-major 专注于绚丽的歌词动画效果的本地音乐/navidrome/第三方多平台在线音乐播放器 A multi-platform music player for local files, Navidrome, and online streaming ser… · 2026/11/6 1:38:26

AgentCraft的Minecraft Mod是如何造出来的:把AI日志变成实时办公室场景全解析
AgentCraft的Minecraft Mod是如何造出来的:把AI日志变成实时办公室场景全解析

【免费下载链接】agentcraft Manage your real AI agents, using Minecraft as your interface! 项目地址: https://gitcode.com/gh_mirrors/agen/agentcraft 点击查看 免费下载 AgentCraft 是一个开源 Minecraft Mod(配合 Foreman 编排器)&… · 2026/11/6 1:38:26

Terra Packages 完全指南:Fedora 滚动更新仓库如何让你一键用上 1300+ 软件包的最新版本
Terra Packages 完全指南:Fedora 滚动更新仓库如何让你一键用上 1300+ 软件包的最新版本

【免费下载链接】packages Monorepo for Terra Packages 项目地址: https://gitcode.com/gh_mirrors/packages28/packages 点击查看 免费下载 Terra Packages 是一个面向 Fedora 用户的滚动更新软件仓库(rolling-release repository)&#x… · 2026/11/6 1:38:26

如何 10 分钟把微信聊天记录做成网页存档:留痕(WeChatMsg)导出工具全流程
如何 10 分钟把微信聊天记录做成网页存档:留痕(WeChatMsg)导出工具全流程

如何 10 分钟把微信聊天记录做成网页存档:留痕(WeChatMsg)导出工具全流程 【免费下载链接】WeChatMsg 提取微信聊天记录,将其导出成HTML、Word、CSV文档永久保存,对聊天记录进行分析生成年度聊天报告 项目地址: http… · 2026/11/6 1:38:26

水电费收到了反馈
水电费收到了反馈

水电费水电费水电费首付款水电费 收到 · 2026/11/6 1:38:26

把“怎么做研究”写进文件:从 scientific-agent-skills 看科研 Agent 的程序性知识层
把“怎么做研究”写进文件:从 scientific-agent-skills 看科研 Agent 的程序性知识层

目录 一、一个没有运行时的仓库为什么会登上周榜 (一)从一周七千余星说起 (二)问题与结论说明 二、科研 Agent 工具化的三代路径 (一)第一代:领域工具集 (二)第二代… · 2026/11/6 1:37:26

双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/5 10:27:59

STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/5 10:28:03

DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/5 10:28:07

三菱PLC数据采集到数据库的时序设计:Python+MC协议实战
三菱PLC数据采集到数据库的时序设计:Python+MC协议实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/6 1:02:11

ASIC主模式RTL设计:状态机与三态驱动的流片级实现
ASIC主模式RTL设计:状态机与三态驱动的流片级实现

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/6 1:02:11

STM32嵌入式C++开发:GDB调试与Renode仿真实战指南
STM32嵌入式C++开发:GDB调试与Renode仿真实战指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/6 1:02:11

双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/5 10:27:59

STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/5 10:28:03

DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/5 10:28:07

了解更多?预约专属演示

我们的顾问将为您一对一讲解产品与方案

企业微信二维码