缓存穿透、击穿、数据一致性解决方案

一、缓存雪崩

大量的key设置了相同的过期时间,导致在缓存在同一时刻全部失效,造成瞬时DB请求量大、压力骤增,引起雪崩。
解决方案:
1.缓存时间设置随机值,尽量让失效时间点均匀分布
2.在缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量
3.做二级缓存,或者双缓存策略。A1为原始缓存,A2为拷贝缓存,A1失效时,可以访问A2,A1缓存失效时间设置为短期,A2设置为长期
4.Redis可以使用高可用架构(Sentinel),主备切换。

二、缓存穿透

查询一定不存在的数据,这样不能命中缓存,直接查询数据库
解决方案:
1.接口限流与熔断、降级,封掉恶意访问的IP
2.使用互斥锁排队
3.使用布隆过滤器,不存在直接返回
4.缓存空值,设置短暂过期时间

三、缓存与数据库双写情况:

A. 先更新数据库,再删除缓存
B. 先删缓存,再更新数据库
C. 先更新数据库,再更新缓存
D. 先更新缓存,再更新数据库
E. 先删除缓存,再更新数据库,再删除缓存

四、分析

4.1 A情况,相比较而言最好的方案

存在的问题:

问题1: 更新数据库成功,更新缓存失败 数据不一致
这时候需要根据业务是否需要保持强一致性进行处理,要么该次请求报错,要么等下次缓存重新刷新

问题2:线程1是读请求,线程2是写请求,在线程1读缓存时,缓存失效,查询数据库,线程2进行写数据库,并且写提交后删除缓存,这时线程1将读取的数据设置缓存,这时的缓存就是原始旧数据
这种情况不常见,需要满足两个条件:

  1. 读数据时缓存恰好失效
  2. 写操作比读操作先完成(一般数据库的写比读更耗时)

4.2 B情况

存在的问题:

问题1:删除缓存成功,更新数据库,并发高的情况对数据库压力巨大

问题2:线程1是写请求,删除缓存,写数据库,线程2读请求,发现无缓存,查库,设置缓存,线程2更新数据库,出现数据不一致
可以使用延时删除缓存策略:先删缓存,更新数据库后,休眠1秒(根据业务时间确定)再删除缓存

问题3: 存在和A情况一样删除缓存失败场景

4.3 C,D情况

存在的问题:

存在两个写请求,本来执行顺序是A,B,但是由于网络或者业务处理耗时不同,造成B,A,数据一致性被破坏

这种情况,可以加分布式锁来解决,但是锁会牺牲性能,根据业务情况选择合适锁粒度,或者更新操作在业务上不区分先后,上面因为并发问题都可以通过锁解决。
同时更新数据库和缓存都可能存在失败情况,需要考虑。

4.4 E情况

存在的问题:
A存在的问题,E都存在,没有必要删两次缓存,多一次删缓存还会让读请求查询缓存旧值概率变大。

4.5 保持强一致性

更新数据库后,异步删除缓存,失败可以重试。

猜你喜欢

转载自blog.csdn.net/LJJZJ/article/details/89088037