如何应对缓存同步、缓存穿透、缓存击穿、缓存雪崩问题?

117 阅读4分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第2天,点击查看活动详情

使用缓存后带来的问题

常见缓存一致性问题的解决方案

数据实时同步

  • 这种数据同步是增量、主动、强一致性
  1. 对数据库数据进行更新的时候(新增、删除、更新)淘汰缓存(缓存失效)
  2. 读取数据的时候更新缓存,为了避免缓存击穿带来的雪崩问题我们需要做同步处理,控制只有一个线程去读取数据然后更新到缓存,其他线程被阻塞等待。
  3. 设置缓存失效时间,这是一个兜底操作假设在更新缓存失败这个缓存失效时间一到就会把缓存失效

  • 这种数据同步是增量、被动、准一致性

对数据进行更新操作时在更新数据库后发送一个更新缓存的MQ消息(如果要保证数据不丢失,建议可以建立本地一个消息表在发送MQ失败后可以重试)

缓存更新服务消费MQ更新数据消息后读取数据库数据进行相关业务处理

缓存更新服务更新业务处理结果数据到缓存中

任务调度更新

这种通过分布式调度任务进行定时更新缓存,使用场景如:报表统计数据、对账数据定时更新到缓存等实时性要求不高的场景。

  • 实现比较简单
  1. Timer 或 ScheduleExecutorService

  2. Spring Task 定时任务 
    @Scheduled(cron=“0 0 0/1 * * ? ”)//每隔一个小时更新

  3. 定时任务框架Quartz

binlog日志订阅

通过订阅binlog来更新缓存,把我们搭建的消费服务,作为mysql的一个slave,订阅binlog,解析出更新内容,再更新到缓存。

应对缓存穿透、缓存击穿、缓存雪崩

缓存处理流程回顾

数据查询先进行缓存查询,如果key不存在或者key已经过期,再对数据库进行查询,并把查询到的对象,放进缓存。如果数据库查询对象为空,则不放进缓存。

缓存穿透

缓存穿透是指查询一个一定不存在的数据,由于缓存是不命中时需要从数据库查询,查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到数据库去查询,造成缓存穿透。在流量大时,可能DB就挂掉了,要是有人利用不存在的key频繁攻击我们的应用,这就是漏洞。

  • 解决方案
  1. 针对业务场景对请求的参数进行有效性校验,防止非法请求击垮db。如用户id<=0 的直接拦截;
  2. 如果db查询不到数据,保存空对象到缓存层,设置较短的失效时间;
  3. 采用bloom filter保存缓存过的key ,在访问请求到来时可以过滤掉不存在的key ,防止这些请求到db层;

缓存击穿

缓存击穿是指缓存中没有但数据库中有的数据(一般是缓存时间到期),这时由于并发用户特别多,同时读缓存没读到数据,又同时去数据库去取数据,引起数据库压力瞬间增大,造成过大压力。

  • 解决方案
  1. 设置热点数据永不过期;如果缓存数据不设置失效时间的话,就不会存在热点key过期造成了大量请求到数据库。
  2. 加互斥锁;当缓存数据失效时,保证一个请求能够访问到数据库,并更新缓存,其他线程等待并重试;

缓存雪崩

缓存雪崩是指缓存中数据大批量到过期时间,而查询数据量巨大,引起数据库压力过大甚至down机。和缓存击穿不同的是,缓存击穿指并发查同一条数据,缓存雪崩是不同数据都过期了,很多数据都查不到从而查数据库。

  • 解决方案
  1. 缓存数据的过期时间设置随机,防止同一时间大量数据过期现象发生。
  2. 如果缓存数据库是分布式部署,将热点数据均匀分布在不同的缓存数据库中。
  3. 设置热点数据永远不过期。