有道无术,术尚可求,有术无道,止于术。
本系列Redis 版本 7.2.5
源码地址:https://gitee.com/pearl-organization/study-redis-demo
文章目录
- 1. 缓存预热
- 2. 缓存雪崩
- 3. 缓存穿透
- 4. 缓存击穿
1. 缓存预热
关键词:预先加载热点数据
缓存预热是指在服务启动或者流量高峰期之前,提前将数据加载到缓存中。避免因为缓存事先没有数据,大量请求直接落入到数据库中。
可以通过以下几种方式进行缓存预热:
- 应用启动时加载:应用启动还未提供对外服务之前,查询数据库并加载到缓存中
- 运行过程中加载:应用运行过程中,加载热点数据到缓存中
在应用启动时进行加载缓存,是比较常用的一种方式,例如,系统配置、系统参数、菜单等业务数据,都会放在基础应用中,并在启动时全量加载到数据库中。
如果使用的是 Spring Boot
则更加方便,在其启动阶段,提供了各种钩子用于执行一些初始化操作:
- 启动监听事件
@PostConstruct
CommandLineRunner
、ApplicationRunner
InitializingBean. afterPropertiesSet
在运行过程中加载热点数据,可以使用以下方式:
- 定时加载:添加定时任务,在使用高峰期前加载缓存
- 监听:使用事件监听或者消息队列,触发缓存加载
2. 缓存雪崩
关键词:缓存大面积失效
雪崩是指大量雪体崩塌,缓存雪崩是指同一时间大面积的缓存失效,从而导致大量请求直接落到数据库上,导致数据库压力过大甚至崩溃。
发生缓存雪崩的情况一般有:
- 单点故障
- 大量缓存同一时间失效
针对 Redis
单点故障,一般可以使用高可用部署方式,比如哨兵、集群模式。针对大量缓存同一时间失效的情况,一般可以采取以下措施:
- 限流:防止并发请求超过阈值,比如使用
Sentinel
。 - 错开失效时间:为每一个缓存数据设置不同的过期时间,并保持一定的随机性。
- 多级缓存:使用本地缓存+
Redis
缓存,当一级缓存失效时,使用更高效的二次缓存。
3. 缓存穿透
关键词:查询一定不存在的数据
缓存穿透是指访问缓存和数据库都不存在的数据,未命中缓存时需要从数据库查询,数据库中也没有对应的数据则也不会写入缓存。这类请求始终都会去数据库查询,如果请求的量过大或存在恶意攻击,会导致数据库压力过大甚至崩溃。
正常来说,用户不会频繁的去访问不存在数据,如果存在这种情况,很有可能是受到了恶意攻击,除了在服务外部做好安全防护外,服务内部也需要做一些处理措施。
解决方案:
- 安全防护:使用防火墙、防攻击软件拦截恶意请求。
- 访问次数限制:使用限流组件,例如
10
秒内限制访问总次数为3
次。 - 接口校验:访问接口层校验参数是否合法,例如,
ID
为负数时,不去数据库查询直接返回。 - 空值或默认值缓存:当访问缓存和数据库都没有查询到值时,可以将空值或默认值写进缓存。相同的
key
下次查询时,会返回缓存数据,如果是不同的key
,第一次还是会去查询数据库。并且还需要设置过期时间,以免缓存key
越写越多。 - 布隆过滤器:设置白名单,在布隆过滤器存在的数据再执行后续请求,不存在的直接返回。设置黑名单,数据库已查询且不存在的数据放入到布隆过滤器,下次查询在黑名单中时,直接返回。
4. 缓存击穿
关键词:缓存刚好失效
缓存穿透是指缓存中的数据刚好失效时,如果有大量并发请求过来,同时判断到缓存为空,会导致同时去查询数据库,导致数据库压力过大甚至崩溃。
这种情况在旁路缓存模式中容易触发,因为该模式更新数据时会删除缓存。特别是热点缓存数据一旦被删除,就需要考虑缓存被“击穿”的问题。一般使用双检加锁策略,首先检查是否有缓存,没有缓存会执行到查询请求,这里加一个互斥锁,再次检查是否有缓存,两次检查都没有时,则查询数据库,并回写缓存中。
双检加锁伪代码如下:
// 1. 第一次查询缓存
Object value = redisTemplate.opsForValue().get("key001");
if (value != null) {
// 缓存命中
return value;
} else {
// 缓存未命中 添加互斥锁
synchronized (this) {
// 2. 第二次查询缓存
value = redisTemplate.opsForValue().get("key001");
if (value != null) {
// 缓存命中 直接返回
return value;
} else {
// 第二次缓存未命中,查询数据库并加载缓存中
value = "value"; // 模拟查询数据库
redisTemplate.opsForValue().set("key001", "value");
return value;
}
}
}