个人简介:Java领域新星创作者;阿里云技术博主、星级博主、专家博主;正在Java学习的路上摸爬滚打,记录学习的过程~ 个人主页:.29.的博客
缓存穿透是指在使用缓存系统时,恶意或者异常的请求导致缓存无法命中,从而每次请求都需要访问数据库,引发数据库负载过高。 简单易懂地说:客户端请求的数据在缓存和数据库中都不存在,缓存永远不生效,所有请求都打到数据库上,使得数据库负载压力大。
缓存穿透的详细解释
:
缓存命中和穿透: 正常情况下,当一个请求到达时,系统首先检查缓存中是否存在相应的数据。如果缓存中有数据(缓存命中),系统会直接返回该数据,避免了对数据库的访问,提高了响应速度。然而,如果缓存中不存在需要的数据,而且请求频繁,就可能导致缓存穿透问题。
缓存穿透的原因: 缓存穿透通常发生在用户请求一个不存在于缓存中的数据,而且这个数据在数据库中也不存在。攻击者可以通过构造恶意请求,故意请求不存在的数据,使得每次请求都绕过缓存直接访问数据库。由于这些请求都是无效的,数据库会返回空结果,但是缓存每次都会被穿透,导致数据库负载过高,降低系统性能。
缓存穿透和缓存击穿的区别:
解决 缓存穿透 的常见方案
:
②缓存空对象(缓存空值): 当系统判断某个数据在数据库中不存在时,可以将这个结果缓存起来,并设置一个较短的过期时间,防止攻击者持续请求同一个无效的数据。
案例
:
@Resource
private StringRedisTemplate stringRedisTemplate;
// 根据id查询商铺信息(缓存空值,避免缓存穿透问题)
@Override
public Result queryById(Long id) {
// redis缓存的key
String key = RedisConstants.CACHE_SHOP_KEY + id;
//1. 从redis缓存中获取shop信息
String shopJSON = stringRedisTemplate.opsForValue().get(key);
//2. 缓存存在,返回
if(StrUtil.isNotBlank(shopJSON)){
Shop shop = JSONUtil.toBean(shopJSON, Shop.class);
return Result.ok(shop);
}
//避免了缓存穿透,获取到空字符串"",直接返回错误
if(shopJson != null){
Result.fail("商铺不存在!");
}
//3. 缓存未命中,从数据库中获取
Shop shop = this.getById(id);
//4. 数据库中不存在,空值写入Redis,返回错误
if(shop == null){
// 控制写入Redis,设置2分钟有效期
stringRedisTemplate.opsForValue().set(key, "", 2L, TimeUnit.MINUTES);
//返回错误
return Result.fail("商铺不存在!");
}
//5. 数据库中存在,存入redis缓存
stringRedisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(shop), 30L, TimeUnit.MINUTES);
//6. 返回
return Result.ok(shop);
}