当前位置: 首页 > news >正文

Redis 实战:缓存、分布式锁、过期策略、防穿透击穿

Redis 实战:缓存、分布式锁、过期策略、防穿透击穿

没有缓存的系统就像没有电梯的百层大楼——用户爬到一半就跑了。Redis 就是这部电梯,用对了,TPS 直接起飞。

一、Redis 在 SpringBoot 中的四重身份

Redis 不只是个"存 key-value 的内存数据库",在 SpringBoot 生态中它承担多种角色:

  • 缓存层:挡在数据库前面,热点数据毫秒级返回
  • 分布式锁:多实例环境下保证同一时刻只有一个线程执行关键代码
  • 会话管理:Spring Session 集成,告别粘性会话
  • 排行榜/计数器:Sorted Set 天然支持排名,INCR 原子递增

二、SpringBoot 整合 Redis

2.1 添加依赖

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency>

2.2 配置文件

spring:data:redis:host:localhostport:6379database:0timeout:3000ms

2.3 RedisTemplate 序列化配置

默认的 JDK 序列化存在两个问题:可读性差(一堆乱码)且占用空间大。推荐改用 JSON 序列化:

@ConfigurationpublicclassRedisConfig{@BeanpublicRedisTemplate<String,Object>redisTemplate(RedisConnectionFactoryfactory){RedisTemplate<String,Object>template=newRedisTemplate<>();template.setConnectionFactory(factory);// Key 用 String 序列化(可读性好)template.setKeySerializer(newStringRedisSerializer());// Value 用 JSON 序列化template.setValueSerializer(newGenericJackson2JsonRedisSerializer());template.setHashKeySerializer(newStringRedisSerializer());template.setHashValueSerializer(newGenericJackson2JsonRedisSerializer());returntemplate;}}

三、Spring Cache 缓存注解三件套

Spring 提供了声明式缓存注解,无需手动写 get/set 代码:

注解作用触发时机
@Cacheable查缓存,有则返回,无则查库并放入缓存方法调用前
@CacheEvict删除缓存方法调用后
@CachePut强制更新缓存(不管 key 是否存在都执行方法并写缓存)方法调用后
@ServicepublicclassUserService{@Cacheable(value="user",key="#id")publicUsergetUserById(Longid){// 先查缓存,命中直接返回;未命中则执行方法并缓存结果returnuserMapper.selectById(id);}@CachePut(value="user",key="#user.id")publicUserupdateUser(Useruser){userMapper.updateById(user);returnuser;}@CacheEvict(value="user",key="#id")publicvoiddeleteUser(Longid){userMapper.deleteById(id);}}

四、缓存三大经典问题:穿透、击穿、雪崩

这是面试高频题,也是生产环境最容易出事故的缓存场景。

4.1 缓存穿透

场景:查询一个数据库中永远不存在的数据(比如 id = -1),每次请求都穿过缓存直接打到数据库。

解决方案

  • 布隆过滤器:在缓存前面加一层布隆过滤器,不存在的 key 直接拦截。Redis 4.0 以上支持BF.ADD命令
  • 空值缓存:把 null 结果也存进缓存,设置极短的过期时间(如 60 秒)
@Cacheable(value="user",key="#id",unless="#result == null")publicUsergetUserById(Longid){Useruser=redisTemplate.opsForValue().get("user:"+id);if(user!=null)returnuser;// 查数据库user=userMapper.selectById(id);if(user!=null){redisTemplate.opsForValue().set("user:"+id,user,30,TimeUnit.MINUTES);}else{// 空值缓存,防止穿透redisTemplate.opsForValue().set("user:"+id,newUser(),1,TimeUnit.MINUTES);}returnuser;}

4.2 缓存击穿

场景:某个热点 key 在过期的瞬间,大量并发请求同时打到数据库。

解决方案

  • 互斥锁:第一个线程获得锁并查库更新缓存,其他线程等待
  • 逻辑过期:不设物理过期,值里包含过期时间戳,后台线程异步刷新

4.3 缓存雪崩

场景:大量 key 在同一时间过期,缓存集体失效,数据库瞬间被压垮。

解决方案:给过期时间加上随机值,比如30 分钟 + random(1-5 分钟)

intrandomMinutes=newRandom().nextInt(5)+1;redisTemplate.opsForValue().set(key,value,30+randomMinutes,TimeUnit.MINUTES);

五、分布式锁:从 SETNX 到 Redisson

5.1 为什么需要分布式锁?

单机环境下synchronizedReentrantLock能搞定并发问题。但服务一部署多个实例,JVM 锁就失效了——锁信息不在同一个 JVM 里。这时需要一个所有实例都能访问的"公共锁"——Redis。

5.2 Redisson 分布式锁(推荐)

手写 SETNX + 过期时间容易出死锁、误删锁等问题。Redisson 是 Redis 官方推荐的 Java 客户端,自带看门狗机制——自动续期,防止业务执行时间超过锁过期时间导致锁被误释放。

<dependency><groupId>org.redisson</groupId><artifactId>redisson-spring-boot-starter</artifactId><version>3.27.0</version></dependency>
@ServicepublicclassOrderService{@AutowiredprivateRedissonClientredissonClient;publicvoiddeductStock(LongproductId){StringlockKey="lock:stock:"+productId;RLocklock=redissonClient.getLock(lockKey);try{// 尝试加锁,最多等待10秒,锁30秒后自动释放if(lock.tryLock(10,30,TimeUnit.SECONDS)){// 查询库存intstock=getStockFromDB(productId);if(stock<=0)thrownewRuntimeException("库存不足");// 扣减库存updateStock(productId,stock-1);}}catch(InterruptedExceptione){Thread.currentThread().interrupt();}finally{// 判断锁是否仍被当前线程持有再释放,防止误删if(lock.isHeldByCurrentThread()){lock.unlock();}}}}

看门狗机制:tryLock第三个参数是锁过期时间。如果不传(使用单参数重载),Redisson 默认设 30 秒,然后每 10 秒自动续期一次。即便业务执行了 2 分钟,锁也不会过期。

六、完整示例:缓存查询 + 分布式锁防击穿

把缓存和分布式锁结合起来,做一道"防击穿缓存查询":

publicUsergetUserByIdWithCache(Longid){StringcacheKey="user:"+id;// 1. 先查缓存Useruser=(User)redisTemplate.opsForValue().get(cacheKey);if(user!=null)returnuser;// 2. 缓存未命中,加分布式锁StringlockKey="lock:user:"+id;RLocklock=redissonClient.getLock(lockKey);try{if(lock.tryLock(5,10,TimeUnit.SECONDS)){// 3. 双重检查user=(User)redisTemplate.opsForValue().get(cacheKey);if(user!=null)returnuser;// 4. 查数据库user=userMapper.selectById(id);if(user!=null){redisTemplate.opsForValue().set(cacheKey,user,30,TimeUnit.MINUTES);}}}catch(InterruptedExceptione){Thread.currentThread().interrupt();}finally{if(lock.isHeldByCurrentThread())lock.unlock();}returnuser;}

七、缓存使用最佳实践总结

实践要点说明
缓存什么读多写少的数据(字典、配置、商品详情)
不缓存什么实时性要求高、频繁修改、超大对象
过期时间热点数据 30min,普通数据 5-10min,随机偏移防雪崩
Key 命名业务:标识:IDuser:info:123,统一规范便于管理
序列化生产环境用 JSON,避免 JDK 序列化的性能和可读性问题
监控关注命中率,低于 90% 需要排查

Redis 用好是银弹,用不好是毒药。缓存更新的策略(先删缓存还是先更数据库)没有银弹,根据业务场景选择。分布式锁务必考虑超时和死锁场景,Redisson 的看门狗能救你半条命。

http://www.jsqmd.com/news/1290488/

相关文章:

  • 2026年7月值得信赖的留坝团建推荐:秦岭深处的企业拓展地 - 装修教育财税推荐2026
  • 找淄博信誉好的手工粉条生产厂家看这儿就对了 - 热点品牌推荐
  • 北京结婚20多年,现在双方都希望尽量协商离婚,不想长期诉讼,但名下有公司、多套房、理财和共同债务,想找北京能做复杂离婚谈判的婚姻家事律所推荐 - 品牌深度评测
  • 以机器学习为基础的房价预测分析研究123(设计源文件+万字报告+讲解)(支持资料、图片参考_相关定制)_
  • G-Helper终极解决方案:告别臃肿控制软件,一键解锁华硕笔记本全功能
  • 【数字信号处理含matlab代码】第三篇:线性相位 FIR 滤波器(二)——Type-3 与 Type-4 及自动识别
  • 2026年月饼包装盒主流供应商推荐排行榜单 - 起跑123
  • 【单片机毕业设计】基于 STM32 的多传感器室内空气实时可视化监测系统,基于单片机继电器控制的室内空气净化调控装置设计(010101)
  • 【路径规划】基于A星算法求解自定义起点终点障碍路径规划问题matlab代码
  • 2026实地走访场景里聊聊滚塑制品厂家哪家好 - 起跑123
  • 2026保姆级指南:免费无水印人像抠图工具大全,电脑手机在线本地软件手把手教学 - 工具软件使用方法推荐
  • 【AI服装更换技术实战指南】:2024年最精准、最低成本的5步换装工作流(附开源模型对比数据)
  • 2025年3月最新松江区木地板打蜡/家庭保洁横向测评排行榜:5家主流机构服务链全流程对比 - mobible
  • 减振器缝焊机常见问题解答(2026专家版) - 全域品牌推荐
  • 2026北京系统窗隔音门窗供应商怎么选?实用选购指南+避坑攻略 - mobible
  • 体验家 XMPlus 酒店住宿行业客户体验管理:预订到离店全旅程体验数据闭环设计
  • 镇海钣金切割折弯焊接一站式制造厂怎么选?看这几点不吃亏 - 热点品牌推荐
  • 【GNSS】GPS M码现代化(三):从“达标”到“够用”——24颗卫星的“及格线”为何填不平27颗的“真实需求”
  • AI 自动化工具 OpenClaw 部署实录,文件处理与桌面任务测试指南(含安装包)
  • 中压电力电缆生产商哪家靠谱?安徽本地企业深度解析 - 热点品牌推荐
  • 2026在宁波挑选产品结构设计公司的实用参考方向 - 起跑123
  • 轿厢式别墅梯源头厂家推荐怎么选更省心 - 热点品牌推荐
  • 定时任务开发:@Scheduled 与分布式定时方案
  • 【图像检测】基于形态学实现红细胞的识别和放大后的边缘检测matlab代码
  • 美标钻尾丝长期合作工厂推荐怎么选才不踩坑 - 热点品牌推荐
  • 2026年寄快递怎么省钱?这5个隐藏技巧让你运费直接砍半 - 快递物流资讯
  • 超强抗风卷帘门,直面台风不变形
  • 2026 年现阶段,淮安口碑好的马路护栏定制厂家推荐,过马路总忽略的这东西,竟能在关键时刻救命? - 企业推荐管【认证】
  • 全球高压缩运动服市场未来盈利分析与投资策略建议报告2026年版
  • 郑州找专业汉白玉玉石漆品牌厂商 实用选购全攻略 - 热点品牌推荐