


当缓存和数据库都没有查询到请求时,就往缓存中存入null,并且设置ttl

热点key一般都会提前写入缓存,所以不用担心缓存穿透的问题




public Shop queryWithMutex(Long id) {
String key = CACHE_SHOP_KEY + id;
//从Redis中查询缓存
String shopString = stringRedisTemplate.opsForValue().get(key);
//判断缓存是否存在
if (StrUtil.isNotBlank(shopString)) {
//请求命中,直接返回
//先把JSON转化为对象
return JSONUtil.toBean(shopString, Shop.class);
}
//解决了缓存穿透
if (shopString == "") {
return null;
}
//缓存未命中
//尝试获取锁
String lockeKey = LOCK_SHOP_KEY + id;
Shop shop = null;
try {
boolean isLock = getLock(lockeKey);
if (!isLock) {
//获取锁失败,休眠一段时间后重新获取
Thread.sleep(50);
queryWithMutex(id);
}
//获取锁成功,查询数据库重建缓存
shop = getById(id);
//判断商铺是否存在
if (shop == null) {
//解决缓存穿透,缓存null
stringRedisTemplate.opsForValue().set(key, "", CACHE_NULL_TTL, TimeUnit.MINUTES);
//不存在
return null;
}
//存在,将数据缓存到Redis
stringRedisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(shop), CACHE_SHOP_TTL, TimeUnit.MINUTES);
} catch (InterruptedException e) {
throw new RuntimeException();
} finally {
//释放互斥锁
deleteLock(lockeKey);
}
return shop;
}
public Shop queryLogicalExpiration(Long id) {
String key = CACHE_SHOP_KEY + id;
//从Redis中查询缓存
String shopString = stringRedisTemplate.opsForValue().get(key);
RedisDate redisDate = JSONUtil.toBean(shopString, RedisDate.class);
LocalDateTime expiration = redisDate.getExpiration();
Shop shop = JSONUtil.toBean((JSONObject) redisDate.getData(), Shop.class);
//判断缓存是否存在
if (StrUtil.isNotBlank(shopString)) {
//未命中
return null;
}
//判断缓存是否过期
if (expiration.isAfter(LocalDateTime.now())) {
//过期直接返回旧数据
return shop;
}
//未过期,尝试获取锁
boolean lockBoolean = getLock(key);
//成功
if (lockBoolean) {
//开启独立线程,更新店铺信息
EXECUTOR_SERVICE.submit(() -> {
try {
this.insertLogical(id, 20L);
} catch (Exception e) {
throw new RuntimeException();
} finally {
deleteLock(key);
}
});
}
//不成功返回旧数据
return shop;
}


1414

被折叠的 条评论
为什么被折叠?



