首先要了解什么是缓存击穿可以看我这一篇:http://t.csdnimg.cn/jMAqw
那么我们复原一下业务场景
@Data
public class RedisData {
private LocalDateTime expireTime;
private Object data;
}
//数据预热
private void saveShop2Redis(long id,long expireSeconds){
//1.查询店铺数据
Shop shop = getById(id);
//2.封装逻辑过期时间
RedisData redisData = new RedisData();
redisData.setData(shop);
redisData.setExpireTime(LocalDateTime.now().plusSeconds(expireSeconds));
//3.写入Redis
stringRedisTemplate.opsForValue().set(CACHE_SHOP_KEY+id,JSONUtil.toJsonStr(redisData));
}
//搭建一个线程池
private static final ExecutorService executor = Executors.newFixedThreadPool(10);
//解决缓存穿透问题
public Shop queryWithLogicalExpiration(long id){
String key = CACHE_SHOP_KEY + id;
//1.先查询Redis
String Jsonshop = stringRedisTemplate.opsForValue().get(key);
//2.判断是否存在
if (StrUtil.isBlank(Jsonshop)){
//不存在 直接返回空
return null;
}
//3.命中 先反序列化为对象
RedisData redisData = JSONUtil.toBean(Jsonshop, RedisData.class);
Shop shop = JSONUtil.toBean((JSONObject) redisData.getData(), Shop.class);
//4.查询是否过期
if (redisData.getExpireTime().isAfter(LocalDateTime.now())){
//5.未过期 直接返回
return shop;
}
//6.过期 重构缓存
String lockKey = LOCK_SHOP_KEY + id;
//6.1获得互斥锁
// 6.2判断是否成功
if(tryLock(lockKey)){
//6.3成功 开启新线程 实现重构缓存
executor.submit(() ->{
//重建缓存
try {
//这里做的就是1.先查数据库2.然后写入Redis
//其实就是重构缓存
this.saveShop2Redis(id,30L);
} catch (Exception e) {
throw new RuntimeException(e);
}finally {
//关锁
unLock(lockKey);
}
});
}
//6.4失败 直接返回旧数据
return shop;
}