消息中间件——Redis—缓存五大问题的解决方案(二十四)

缓存雪崩

缓存雪崩是指某一时刻发生了大规模的缓存失效,比如发生了 Redis 服务器宕机、缓存数据同时到期被删除这种情况,此时大量的请求直接转发到数据库,数据库一旦撑不住就会导致整个服务瘫痪。

  1. 过期时间错峰:

    分析用户行为,制定策略为 key 设置不同的过期时间,尽量让缓存失效的时间均匀分布

  2. 高可用

    采用主从架构 + Sentinel 或者 Redis Cluster 实现HA,避免 Redis 单点故障

  3. 缓存降级

    设置本地缓存(ehcache) + 限流(hystrix)。如果某个目标服务调用慢或者有大量超时,此时,熔断该服务的调用,对于后续调用请求,不再继续调用目标服务,直接返回,快速释放资源。如果目标服务情况好转则恢复调用。服务降级的最终目的是保证核心服务可用

  4. 备份和恢复

    开启 Redis 持久化机制,服务重启后快速恢复缓存数据

缓存击穿

对于一些设置了过期时间的热点 key,需要考虑缓存被“击穿”的问题,这个和缓存雪崩的区别在于这里是针对某个 key 的缓存,前者则是很多 key。

  1. 使用互斥锁(mutex key):

    让一个线程构建 Redis 缓存,其他线程等待构建缓存的线程执行完,然后直接从缓存获取数据

  2. 设置 key 永不过期

    永不过期既要保证 key 一直持有在缓存中,又要能对 key 的 value 进行及时的更新。实现的方式是不为 key 设置物理过期时间,即不通过redis.expire(key, timeout) 命令设置过期时间;但是要设置一个逻辑的过期时间,可以把 key 的过期时间拼接到 value 中,程序访问缓存时,从 value 中解析出过期时间并根据规则判定 key 是否需要更新

两种方案对比:

方案 优点 缺点
使用互斥锁 保证一致性 程序复杂度高,存在死锁和线程阻塞的风险
设置key永不过期 异步构建缓存缓存,线程不会阻塞 无法保证一致性,代码复杂度增加,每个 key 占用的空间增加

缓存穿透

缓存穿透是指查询一个一定不存在的数据。出于容错考虑,如果从存储层查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到存储层去查询,失去了缓存的意义,也很容易被攻击者作为系统漏洞进行攻击。

  1. key校验,key设定一定的规则,不符合规则的倍屏蔽。
  2. 接口层增加校验,如用户鉴权校验,id 做基础校验,id<=0 的直接拦截
  3. 即便存储层查不到这个数据,也将返回的空对象设置到缓存里。下次再请求的时候,直接从缓存取到空对象返回,这种情况一般会将空对象设置一个较短的过期时间,这样可以防止攻击者反复用同一个 id 暴力攻击。

缓存预热

缓存预热就是系统上线后,将相关的缓存数据直接加载到缓存系统,避免在用户请求的时候,先查询数据库,然后再将数据缓存的问题,用户直接查询事先被预热的缓存数据。

文章目录
  1. 1. 缓存雪崩
  2. 2. 缓存击穿
  3. 3. 缓存穿透
  4. 4. 缓存预热
|