缓存击穿问题也叫热点key问题,就是一个被高并发访问并且缓存重建业务比较复杂的key突然失效了,无数的请求访问会在瞬间给数据库带来巨大的冲击
常见的解决方案有两种:
互斥锁(高并发时性能较差)
逻辑过期
基于互斥锁方式解决缓存基穿问题
//解决缓存击穿问题 public Shop queryWithMutex(Long id){ //1.直接从redis中查询商铺id String json = stringRedisTemplate.opsForValue().get(CACHE_SHOP_KEY + id); //互斥锁的key String isKey = "lock:shop:" + id; Shop shop = null; try { //2.判断redis中是否有 if (StringUtils.isNotBlank(json)) { //3.如果有直接返回查询结果 //将json格式转换为对象 shop = JSONUtil.toBean(json, Shop.class); return shop; } if (json != null) {//!=null 就为"" //返回一个错误 return null; } //解决缓存击穿 //4.获取互斥锁 boolean flag = tryLock(isKey); //4.1判断锁是否获取成功 if (!flag) { //4.2休眠 Thread.sleep(50); //4.3未获取 递归调用 return queryWithMutex(id); } // 4.4 成功 根据id查询数据库 shop = this.getById(id); //5.将商铺信息写入redis中 //将sp转化为json格式 String shopJson = JSONUtil.toJsonStr(shop); stringRedisTemplate.opsForValue().set(CACHE_SHOP_KEY + id, shopJson, CACHE_SHOP_TTL, TimeUnit.MINUTES); } catch (InterruptedException e) { throw new RuntimeException(e); } finally { //6.释放锁 unlock(isKey); } //7.返回数据 return shop; } //模拟获取锁 private boolean tryLock(String key){ Boolean flage = stringRedisTemplate.opsForValue().setIfAbsent(key, "1", 10, TimeUnit.SECONDS); return BooleanUtil.isTrue(flage); } //释放锁 private void unlock(String key){ stringRedisTemplate.delete(key); }
通过redis中
Redis Setnx 命令,命令在指定的 key 不存在时,为 key 设置指定的值。来模拟上锁
通过delete模拟释放锁 来达到应有的效果