文章目录
缓存击穿
缓存击穿问题也叫热点Key问题,就是一个被高并发访问并且缓存重建业务较复杂的key突然失效了,无数的请求访问会在瞬间给数据库带来巨大的冲击。
解决方案
- 互斥锁
- 逻辑过期
基于互斥锁方式解决缓存击穿问题
设计思想
代码实现
/**
* 缓存穿透解决方案
*
* @param id id
* @return ResponseEntity<Object>
*/@OverridepublicResponseEntity<Object>getUserByIdWithMutex(String id){// 从redis中根据key查询用户列表信息String user =(String) redisTemplate.opsForValue().get("user:"+ id);// 如果用户信息不为null、""、"\t\n",则存在,转化为Issa,返回if(StrUtil.isNotBlank(user)){Issa issa =JSONUtil.toBean(user,Issa.class);returnResponseEntity.ok(issa);}// 如果user不为null,说明user为空字符串,直接返回if(user !=null){returnResponseEntity.status(400).body("没有用户信息");}Issa userById =null;try{// 获取锁boolean lockFlag =BooleanUtil.isTrue(redisTemplate.opsForValue().setIfAbsent("lock:"+ id,"1",10,TimeUnit.SECONDS));// 如果没有得到锁,则休眠50ms,递归调用该方法if(!lockFlag){Thread.sleep(50);returngetUserByIdWithMutex(id);}// 如果得到了锁,则去数据库查询
userById = userMapper.getUserById(id);// 如果为null,说明数据库没有该数据,为了避免缓存穿透,存上""空字符串,并以TTL过期时间作为兜底方案if(userById ==null){// 将空值写入redis
redisTemplate.opsForValue().set("user:"+ id,"",1,TimeUnit.MINUTES);// 返回错误信息returnResponseEntity.status(400).body("没有用户信息");}// 如果不为null,将该数据缓存到redis服务器
redisTemplate.opsForValue().set("user:"+ id, userById,60,TimeUnit.SECONDS);}catch(InterruptedException e){
e.printStackTrace();}finally{// 最后释放锁
redisTemplate.delete("lock:"+ id);}// okreturnResponseEntity.ok(userById);}
基于逻辑过期方式解决缓存击穿问题
设计思想
代码实现
privatestaticfinalExecutorService CACHE_REBUILD_EXECUTOR =Executors.newFixedThreadPool(10);/**
* 缓存穿透解决方案
*
* @param id id
* @return ResponseEntity<Object>
*/@OverridepublicResponseEntity<Object>getUserByIdWithLogicalExpire(String id){// 从redis中根据key查询用户列表信息String user =(String) redisTemplate.opsForValue().get("user:"+ id);// 如果没有用户信息if(StrUtil.isBlank(user)){returnResponseEntity.status(400).body("没有用户信息");}RedisData redisData =JSONUtil.toBean(user,RedisData.class);Issa issa =JSONUtil.toBean((JSONObject) redisData.getData(),Issa.class);LocalDateTime expireTime = redisData.getExpireTime();if(expireTime.isAfter(LocalDateTime.now())){returnResponseEntity.ok(issa);}boolean lockFlag =BooleanUtil.isTrue(redisTemplate.opsForValue().setIfAbsent("lock:"+ id,"1",10,TimeUnit.SECONDS));if(lockFlag){
CACHE_REBUILD_EXECUTOR.submit(()->{try{// 查询数据库Issa userById = userMapper.getUserById(id);// 重建缓存// 设置逻辑过期RedisData newRedisData =newRedisData();
newRedisData.setData(issa);
newRedisData.setExpireTime(LocalDateTime.now().plusSeconds(TimeUnit.SECONDS.toSeconds(60)));// 写入Redis
redisTemplate.opsForValue().set("user:"+ id,JSONUtil.toJsonStr(newRedisData));}catch(Exception e){thrownewRuntimeException(e);}finally{// 释放锁
redisTemplate.delete("lock:"+ id);}});}// okreturnResponseEntity.ok(issa);}
/**
* 处理逻辑过期时间的类
*
* @author issavior
*/@DatapublicclassRedisData{privateLocalDateTime expireTime;privateObject data;}
**
💥《核心技术系列专栏汇总》💥
**
- 《Java系核心技术》
- 《中间件核心技术》
- 《微服务核心技术》
- 《云原生核心技术》
- 《通用业务实现集》
- 《一起去大厂系列》
- 《开源项目o s s a》
需要
大厂面试题
、
简历模版
、
电子书
、
所有源码
等关注👇【公众号】👇回复「 1024 」即可。
本文转载自: https://blog.csdn.net/CSDN_SAVIOR/article/details/125124941
版权归原作者 步尔斯特 所有, 如有侵权,请联系我们删除。
版权归原作者 步尔斯特 所有, 如有侵权,请联系我们删除。