缓存穿透、缓存击穿、缓存雪崩都有哪些常见处理?
缓存穿透
当查询的数据不存在时,此时就会穿透缓存抵达数据库,但是由于数据库不存在数据,因此缓存依旧为空,当请求该数据的量较大时(或者恶意访问),会造成性能损失。
对于这种问题,我们首先要思考的就是为什么会频繁的访问一个不存在的数据?除了恶意访问外,正常访问中什么样的业务情况会出现频繁访问不存在的数据?想明白这些之后其实对应的解决方法也就很好想了。核心目标就是把不存在数据的无效请求,拦在数据库之前,避免无效请求打满数据库。
以下是一些常见的处理手段:
(一)参数合法性校验
在请求链路的最外层(网关 / Controller 层),拦截明显非法的请求,比如负数 ID、格式错误的参数、超长异常入参,直接返回失败,不进入缓存和数据库逻辑。这样做能拦截 80% 以上的低级无效请求。
(二)对空值进行缓存
当数据库查询结果为空时,依然把这个 key 缓存到 Redis,需要设置一个超时时间,建议 30s~5min,根据业务进行调整。
(三)采用布隆过滤器
我们知道通过布隆过滤器(Bloom Filter)得出的“不存在”结果,就一定是不存在的,请求过来交给布隆过滤器处理,如果结果不存在,直接返回就好。这样能够完美处理缓存穿透的情况。
布隆过滤器的实现可以使用 Redis 布隆过滤器插件、Google Guava 本地布隆过滤器。
布隆过滤器无法删除,需定期重置问题如何处理?
- 通过将 1bit 存储机制改为 3-4bit 存储计数,通过计数的方式可以一定程度上判断是否可以删除,但有最大限制且增加了空间成本。
- 另一种方式就是采用布谷鸟过滤器,通过指纹和两个桶(或多个桶)的机制来判断“肯定不存在”的情况,可以做到安全删除。此处不做详解。
(四)黑名单和限流
这种方式是为了兜底,解决恶意攻击的问题。可以通过网关监控请求行为,对短时间内大量请求无效 key 的 IP,直接加入黑名单封禁;同时对接口设置全局限流(比如单用户每秒 10 次请求),哪怕遭遇缓存穿透,也不会让数据库被打满。
缓存击穿
缓存击穿指的是某一个 hot key 突然失效,导致大量的请求直击数据库,达到以点破面的效果。为了保护数据库,面对这种情况有两种思路,一个是合适的处理这个 hot key 的过期时间,另一个是限流访问数据库。主要方式有以下几种:
(一)hot key 设置永不过期
这种方式最简单,但是会长期占用内存,需要考虑实际的业务场景、更新机制、Redis 内存使用情况与淘汰策略(不会有人用 allkeys-random 策略吧)。采用这种方式的数据必须是不频繁变更的热点数据,同时业务场景允许不过期。更新机制大概可以分为两种:
- 通过后台异步定时任务,主动更新缓存内容,保证缓存和数据库一致。
- 在 hot key 里存一个“逻辑过期时间”,查询时如果到了这个时间,就启动异步任务去更新缓存,需要注意在多实例情况下做好并发控制。
(二)使用互斥锁限流
当缓存失效时,只让拿到锁的 1 个请求去查数据库并更新缓存,其他请求自旋等待重试,或者直接返回降级数据,保证同一时间只有极少量请求打到数据库。
(三)服务降级
这也不能算是解决方法吧,就是个纯兜底,如果缓存失效了,又拿不到锁(缓存服务器宕机之类的),为了保护数据库,只能返回兜底的静态数据,或者提前保存快照数据返回。实在不行就是提示用户“服务繁忙,请稍后再试”。
缓存雪崩
在缓存的使用过程中,如果同一时间大量的缓存失效,或者缓存服务器宕机了,导致大量的请求直击数据库,此时称为缓存雪崩。那我们要做的核心目标就是从架构上避免缓存层整体失效,哪怕出现故障,也不会让数据库被全量打满。主要的处理方式如下:
(一)为不同 key 设置不同时间
在固定过期时间基础上,增加一个随机时间,并定时更新。比如原本统一设置 24 小时过期,改成24 小时 + 随机 0~30 分钟。这种方式最简单,能解决绝大部分场景下的雪崩问题。
(二)采用多层缓存架构
用本地缓存存放高频热点数据,例如 Caffeine、Guava Cache,分布式缓存做集群避免单点故障。需要做好多层缓存的数据一致性,更新数据库时要同步更新 / 删除各级缓存。
(三)做冷热分离,将热点数据设置为永不过期
该方法需要进行业务评估,如果允许热点数据设置为永不过期,可以采用异步任务的方式进行更新数据。参考缓存击穿的处理方式。
(四)使用队列进行削峰
该方式适合一些非实时性的业务场景,当缓存失效时,将数据库访问操作放进队列中,再进行合适的匀速访问,也可以做批量操作合并成一次访问更新,保证在数据库的承受范围内。需要根据业务情况进行定制。
总结
对于很多新手朋友们总是很难记住他们之间的区别,我也经历过。在这里分享一个以前自己记忆的小办法,就是(0,1,n)。
0:就是数据不存在,会穿透。
1:就是 1 个 key 失效,会击穿。
n:就是 n 个 key 失效,雪崩了。