黑马点评的总结和反思
1.缓存穿透
问题的体现
*下面就是我们的解决的方法(一旦查询到一次不存在的值,就往redis里面放入我们的空字符串这样下次访问无效的数据就可以使用redis来返回空字符串来防止数据库的损伤)
下面就是解决方案(直接给出最终的工具类因为工具类就可以通用了)
2.解决缓存击穿
问题的体现
解决思路
实现的思路
也是使用工具类的解决方法
首先我创建了一个类里面有我们一个新的字段就是逻辑过期的时间的设置RedisDate
// TODO 防止缓存击穿的线程池
// TODO 成功 就开启一个线程(实现缓存重建)
// 使用线程池来开启线程(10个线程)
private static final ExecutorService CACHE_REBUILD_EXECUTOR = Executors.newFixedThreadPool(10);
/**
* 这也是防止缓存击穿(设置的逻辑过期)
*
* @param keyPrefix 表示前缀
* @param id 表示我们要查询数据库的id
* @param type 表示我们从redis中的得到的数据转为什么java对象
* @param dbFallback 函数式方程
* @param time 设置过期的时间
* @param unit 设置过期的单位
* @param <R> 这个就是我们的返回值
* @param <ID> 这个就是我们id的类型
* @return
*/
public <R, ID> R queryWithLogiclExpire(
String keyPrefix, ID id, Class<R> type, Function<ID, R> dbFallback, Long time, TimeUnit unit) {
String key = keyPrefix + id;
// 取redis中的缓存
String jsonStr = stringRedisTemplate.opsForValue().get(key);
// 进了这个判断就是没有命中
if (StrUtil.isBlank(jsonStr)) { //isNotBlank 不为空就是true
return null;
}
// 命中需要json反序列化
RedisData redisData = JSONUtil.toBean(jsonStr, RedisData.class);
// 获取我们的数据
// 得到我们的jsonObject
JSONObject data = (JSONObject) redisData.getData();
// 得到我们json对象
R r = JSONUtil.toBean(data, type);
// 获取过期时间(逻辑过期时间)
LocalDateTime expireTime = redisData.getExpireTime();
// 再来判断是否过期(过期时间是不是在当前时间之后喃)
if (expireTime.isAfter(LocalDateTime.now())) {
// 没有过期直接返回
return r;
}
// 如果已经过期 我们就要进行缓存重建
String lockKey = LOCK_SHOP_KEY + id;
// 获取互斥锁
boolean isLock = tryLock(lockKey);
// 判断获取锁是否成功
if (isLock) {
// TODO 成功 就开启一个线程(实现缓存重建)
// 提交任务
CACHE_REBUILD_EXECUTOR.submit(() -> {
try {
// TODO 重建缓存
// 第一步查询数据库
R r1 = dbFallback.apply(id);
// 写入redis(并且使用的是逻辑过期)
this.setWithLogiclExpire(key, r1, time, unit);
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
// 重建缓存后就要释放锁
unlock(lockKey);
}
});
}
// 不成功就返回当前的数据(也就是旧数据)
// 返回
return r;
}
下面是我们利用锁(也是利用了redis的一个特性)
关闭锁当然也就很简单了