springboot:整合redis解决缓存击穿,缓存雪崩,缓存穿透
一、缓存穿透
一个在缓存和数据库都不存在的数据,而用户不断发起请求,借此攻击数据库,造成数据库压力过大。比如请求 id < 0 的数据
解决方案:
- 接口校验、限流
- 布隆过滤器
- 缓存空值,设置过期时间短些
@GetMapping("/penetrate") public String cachePenetrate(Integer id) { String cacheKey = "penetrate:" + id; long cacheTime = 30L; //缓存查询 String cacheValue = (String) redisTemplate.opsForValue().get(cacheKey); if (cacheValue == null) { //缓存没有,查询数据库 Product product = productService.getById(id); if (product == null) { //数据库没有,设置空值或默认值 cacheValue = ""; } else { cacheValue = product.getName(); } redisTemplate.opsForValue().set(cacheKey, cacheValue, cacheTime, TimeUnit.SECONDS); } return cacheValue; }
二、缓存击穿
一个key数据库存在,原来缓存有但现在过期了,发送请求发现过期了直接查询数据库并回设缓存,此时若发生高并发可能会瞬间把数据库压垮
解决方案:
- 设置key永不过期
- 队列排队
- 互斥锁
@GetMapping("/puncture") public String cachePuncture(Integer id) { String cacheKey = "puncture:" + id; long cacheTime = 30L; //缓存查询 String cacheValue = (String) redisTemplate.opsForValue().get(cacheKey); if (cacheValue == null) { //缓存没有,使用互斥锁查询数据库更新缓存,其余阻塞排队 synchronized (cacheKey) { //此时可能有缓存数据了 cacheValue = (String) redisTemplate.opsForValue().get(cacheKey); if (cacheValue == null) { //缓存还是没有,查询数据库 Product product = productService.getById(id); cacheValue = product.getName(); //回设缓存 redisTemplate.opsForValue().set(cacheKey, cacheValue, cacheTime * 10, TimeUnit.SECONDS); } } } return cacheValue; } public String cachePuncture2(Integer id) throws InterruptedException { String cacheKey = "puncture:" + id; String blockKey = "block:" + id; long cacheTime = 30L; //缓存查询 String cacheValue = (String) redisTemplate.opsForValue().get(cacheKey); if (cacheValue == null) { //setIfAbsent == SETNX :只有key不存在的时候才能设置成功,利用它可以实现锁的效果 if (redisTemplate.opsForValue().setIfAbsent(blockKey, "1", cacheTime, TimeUnit.SECONDS)) { //查询数据库 Product product = productService.getById(id); cacheValue = product.getName(); //回设缓存 redisTemplate.opsForValue().set(cacheKey, cacheValue, cacheTime * 10, TimeUnit.SECONDS); redisTemplate.delete(blockKey); } else { //阻塞一会,再重试获取数据 Thread.sleep(50); return cachePuncture2(id); } } return cacheValue; }
三、缓存雪崩
当缓存服务器重启或者大量缓存集中在某一时间段失效,这样在失效的时候,也会给数据库带来很大压力。跟缓存击穿不一样,雪崩是大量key集体过期
解决方案:
- 热点数据不过期
- 在原有的失效时间基础上增加一个随机值,减少集体失效
- 加锁排队
@GetMapping("/avalanche") public String cacheAvalanche(Integer id) { String cacheKey = "avalanche:" + id; long cacheTime = 30L; //缓存查询 String cacheValue = (String) redisTemplate.opsForValue().get(cacheKey); if (cacheValue == null) { //缓存没有,使用互斥锁查询数据库更新缓存,其余阻塞排队 synchronized (cacheKey) { //此时可能有缓存数据了 cacheValue = (String) redisTemplate.opsForValue().get(cacheKey); if (cacheValue == null) { //缓存还是没有,查询数据库 Product product = productService.getById(id); cacheValue = product.getName(); //回设缓存 redisTemplate.opsForValue().set(cacheKey, cacheValue, cacheTime * 10, TimeUnit.SECONDS); } } } return cacheValue; } @GetMapping("/avalanche2") public String cacheAvalanche2(Integer id) { String cacheKey = "avalanche:" + id; String signKey = "avalanche:sign" + id; long cacheTime = 60L; //缓存查询 String cacheValue = (String) redisTemplate.opsForValue().get(cacheKey); //缓存标记 String signValue = (String) redisTemplate.opsForValue().get(signKey); if (signValue == null) { //缓存标记过期 //设置成功的去查询数据库并更新缓存,其余的返回旧的缓存值(缓存值的时间是缓存标记的2倍) if (redisTemplate.opsForValue().setIfAbsent(signKey, "1", cacheTime, TimeUnit.SECONDS)) { //查询数据库 Product product = productService.getById(id); cacheValue = product.getName(); redisTemplate.opsForValue().set(cacheKey, cacheValue, cacheTime * 2, TimeUnit.SECONDS); } } return cacheValue; }
这里第一种方法,采用加锁排队有可能还要解决分布式锁的问题,线程还会被阻塞,用户体验很差
第二种方法:
缓存标记:记录缓存数据是否过期,如果过期就去更新实际key的缓存;
缓存数据:它的过期时间比缓存标记的时间延长1倍。这样,当缓存标记过期后,实际缓存还能把旧数据返回给调用端,直到新的key值更新完成后,才会返回新缓存
————————————————
版权声明:本文为CSDN博主「yololee_」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_43296313/article/details/125447527
吾乃代码搬运工,侵联删
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· DeepSeek 开源周回顾「GitHub 热点速览」
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了