什么是缓存击穿?如何解决?

什么是缓存击穿?如何解决?1 什么是缓存击穿 缓存击穿问题也叫热点 Key 问题 就是一个被高并发访问并且缓存重建业务较复杂 意味着对数据库压力相对较大 的 key 突然失效了 可以理解为 redis 的缓存突然无了 无数的请求访问会在瞬间给数据库带来巨大的冲击

大家好,欢迎来到IT知识分享网。

缓存击穿(热点key问题)

1.什么是缓存击穿?

缓存击穿问题也叫热点Key问题,就是一个被高并发访问并且缓存重建业务较复杂(意味着对数据库压力相对较大)的key突然失效了(可以理解为redis的缓存突然无了),无数的请求访问会在瞬间给数据库带来巨大的冲击。

2.解决方案

1.互斥锁:当同个业务不同线程访问redis未命中时,先获取一把互斥锁,然后进行数据库操作,此时另外一个线程未命中时,拿不到锁,等待一段时间后重新查询缓存,此时之前的线程已经重新把数据加载到redis之中了,线程二就直接缓存命中。这样就不会使得大量访问进入数据库

2.优缺点

优点:没有额外的内存消耗,保证一致性,实现简单

缺点:线程需要等待,性能受影响,可能有死锁风险

2.逻辑过期:给缓存设置一个逻辑过期时间,什么意思呢?缓存本来在redis之中,正常情况下除了主动更新它是不会变的,为了防止缓存击穿,我们以一种预判或者说保守的方式,主动设置一个过期时间,当然这个时间过期了,缓存里面的数据是不会消失的,但是我们只需要根据这个假设的过期时间。来进行经常的动态的缓存数据的更新。可以对缓存击穿起一定的预防作用

优点:线程无需等待,性能较好

缺点:不保证一致性,有额外内存消耗,实现复杂

实现代码

//互斥锁 public Shop queryWithMutex(Long id){ //1.从redis查询商铺缓存 String shopJson = stringRedisTemplate.opsForValue().get(RedisConstants.CACHE_SHOP_KEY + id); //2.判断是否存在 if(StrUtil.isNotBlank(shopJson)){ //3.存在直接返回 Shop shop = JSONUtil.toBean(shopJson, Shop.class); return JSONUtil.toBean(shopJson,shop.getClass()); } /* 判断命中的是否为空 */ if (shopJson!=null){ //返回一个错误信息 return null; } //4,实现缓存重建 //4.1 获取互斥锁 String lockKey="lock:shop"+id; Shop shop = null; try { boolean isLock = tryLock(lockKey); //4.2 判断是否获取成功 if (!isLock){ //4,3 失败 则休眠并重试 Thread.sleep(50); queryWithMutex(id); } //4.4 成功 根据id查询数据库 shop = getById(id); // 5.不存在 返回错误 if (shop==null){ //将空值写入redis(防止缓存击穿问题) stringRedisTemplate.opsForValue().set(RedisConstants.CACHE_SHOP_KEY+ id,"",2L,TimeUnit.MINUTES); return null; } // 6.存在,写入redis stringRedisTemplate.opsForValue().set(RedisConstants.CACHE_SHOP_KEY+ id,JSONUtil.toJsonStr(shop),RedisConstants.CACHE_SHOP_TTL, TimeUnit.MINUTES); } catch (InterruptedException e) { throw new RuntimeException(e); } finally { // 7,释放互斥锁 unlock(lockKey); } // 7.返回 return shop; }
//逻辑过期解决方案 public Shop queryWithLogicalExpire(Long id){ //1.从redis查询商铺缓存 String shopJson = stringRedisTemplate.opsForValue().get(RedisConstants.CACHE_SHOP_KEY + id); //2.判断是否存在 if(StrUtil.isBlank(shopJson)){ //3如果不存在 直接返回null return null; } //4命中 需要先把json反序列化为对象 RedisData redisData = JSONUtil.toBean(shopJson, RedisData.class); JSONObject data = (JSONObject)redisData.getData(); Shop shop = JSONUtil.toBean(data, Shop.class); LocalDateTime expireTime = redisData.getExpireTime(); //判断是否过期 if (expireTime.isAfter(LocalDateTime.now())){ // 5.1 未过期 直接返回商铺信息 return shop; } // 5.2 已过期 需要缓存重建 // 6.缓存重建 // 6.1获取互斥锁 String lockKey="lock:shop"+id; boolean isLock = tryLock(lockKey); // 6.1判断是否获取锁成功 if(isLock){ //todo 6.2成功 开启独立线程实现缓存重建 CACHE_REBUILD_EXECUTOR.submit(()->{ try { //重建 this.saveShopRedis(id,20L); } catch (Exception e) { throw new RuntimeException(e); } finally { //释放锁 unlock(lockKey); } }); } // 6.3失败 ,直接返回旧的商铺 return shop; }

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/126932.html

(0)
上一篇 2025-09-14 20:00
下一篇 2025-09-14 20:10

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注微信