缓存穿透的解决方案
什么是缓存穿透?
缓存穿透是指客户端请求的数据在缓存中和数据库中都不存在,
这样缓存永远不会生效,这些请求都会打到数据库。
解决方案
- 缓存空字符串(即一个默认值)
- 优点:实现简单,维护方便
- 缺点:额外的内存消耗,可能造成短期的不一致
- 布隆过滤
- 优点:内存占用较少,没有多余key
- 缺点:实现复杂,存在误判可能(有穿透的风险),无法删除数据
实现(这里是缓存一个空字符串)
/*
querywithchuantou 方法旨在解决缓存穿透问题。缓存穿透是指对一个在缓存和数据库中都不存在的数据进行频繁查询,
导致每次请求都直接访问数据库,增加了数据库的压力。
*/
public Shop querywithchuantou(Long id) {
/*
1. 从 Redis 查询缓存:
首先,通过 stringRedisTemplate 从 Redis 中获取键为 CACHE_SHOP_KEY + id 的缓存数据。
这里,CACHE_SHOP_KEY 是缓存键的前缀,id 是店铺的唯一标识符。
*/
String shopJson = stringRedisTemplate.opsForValue().get(CACHE_SHOP_KEY + id);
/*
2. 判断缓存中是否存在数据:
2.1 缓存命中的话,直接返回店铺数据
如果 shopJson 不为空且内容有效(即 shopJson 不是空字符串),
则将其反序列化为 Shop 对象并返回,避免了对数据库的访问。
StrUtil.isNotBlank(shopJson) 用于判断 shopJson 是否不为 null、不为空字符串,
且不全是空白字符。
*/
if (StrUtil.isNotBlank(shopJson)) {
Shop shop = JSONUtil.toBean(shopJson, Shop.class);
return shop;
}
/*
2.2 缓存未命中
*/
if (shopJson != null) {
/*
2.2.1 缓存中为空字符串:
如果 shopJson 不为 null,但前面的 isNotBlank 判断未通过,
说明 shopJson 是一个空字符串或仅包含空白字符。
表明该店铺不存在。因此,直接返回 null,避免再次访问数据库。
*/
return null;
}
/*
2.2.2 当前数据是null,查询数据库:
如果缓存中没有相关数据(即 shopJson 为 null),则查询数据库获取店铺信息。
*/
Shop shop = getById(id);
/*
3. 判断数据库中是否存在该数据:
*/
if (shop == null) {
// 这里的常量值是2分钟
/*
3.1 数据库中也没,则缓存空对象
如果数据库中也没有该店铺的信息,则将空字符串存入 Redis,并设置一个较短的过期时间(CACHE_NULL_TTL,例如 2 分钟)。
这样可以在短时间内避免对同一不存在的数据频繁查询数据库,减轻数据库压力。
*/
stringRedisTemplate.opsForValue().set(CACHE_SHOP_KEY + id, "", CACHE_NULL_TTL, TimeUnit.MINUTES);
return null;
}
/*
3.2 数据库中存在,缓存数据库中存在的数据:
如果数据库中存在该店铺的信息,则将其序列化为 JSON 字符串,存入 Redis,
并设置一个较长的过期时间(CACHE_SHOP_TTL,例如 30 分钟)。这样可以在后续相同的查询中直接从缓存获取数据,提高查询效率。
*/
String jsonStr = JSONUtil.toJsonStr(shop);
// 并存入redis,并设置TTL,防止存了错的缓存
stringRedisTemplate.opsForValue().set(CACHE_SHOP_KEY + id, jsonStr, CACHE_SHOP_TTL, TimeUnit.MINUTES);
// 7. 最终把查询到的商户信息返回给前端
return shop;
}

浙公网安备 33010602011771号