如何快速弄懂缓存穿透、缓存击穿、缓存雪崩之间的区别?

作者:小牛呼噜噜 ,首发于公众号「小牛呼噜噜

什么是缓存穿透、缓存击穿、缓存雪崩

哈喽,大家好呀,我是呼噜噜,今天我们来简单聊聊,关于缓存稳定性的3种经典问题:缓存穿透、缓存击穿、缓存雪崩

聊这3个问题前,我们得知晓缓存最常见的应用模式Cache-Aside Pattern旁路缓存模式

缓存穿透

缓存穿透: 当请求过来,访问不存在的数据时(即既不在缓存中,也不在数据库中),这会导致访问缓存,未命中,继续访问数据库db,然后发现在数据库中还是未查询到数据,这个时候也就不能回写缓存,来为后续的请求服务;也就是说,当这种请求过来,每次都会去查数据库,缓存形同虚设,一旦流量暴增,容易直接带崩数据库

这种不存在的数据可能被管理员误删,也有可能被黑客恶意利用(恶意请求),不断地去试,一旦发现一个不存在的数据,就拼命发请求访问这个数据,直到数据库锁住

那解决办法也很简单,常见的有:

  1. 比如每次访问数据如果既不在缓存中,也不在数据库中,那就缓存一个占位符或者空值过期时间也不要设置过长,比如1分钟就行,这样的话,在1分钟内,这么多请求只有一次能直接访问数据库,这样就能显著降低数据库的压力;如果缓存过期时间过长,会出现大量的空缓存,进而导致缓存资源的浪费
  2. 还可以针对请求携带的参数,比如是那种特殊字符、非法字符等,我们数据库肯定不会存这些东西,直接在应用服务层进行限制,不允许访问
  3. 还可以通过第三方组件来实现,比如布隆过滤器,其主要是其特性:布隆过滤器判断一个元素不在集合中,那肯定就不在。如果判断存在,那有一定可能性它在说谎,具体原理可以参考笔者以前的一篇文章海量数据处理的利器-布隆过滤器。在缓存和数据库之间再加上布隆过滤器,通过布隆过滤器快速判断数据是否存在,从而避免多次之间请求数据库

缓存击穿

在我们正常的业务之中,总有一些数据会被频繁访问,这就是热点数据

所谓的缓存击穿指的是,缓存中热点数据的key过期失效,由于是热点数据,在过期的一瞬间会有大量的请求过来(高并发),这些请求,最终都会直接访问数据库,这样数据库很容易被打垮,缓存仿佛被”击穿”了

常见的解决方案:

  1. 加锁,进程锁/分布式锁,当请求过来时,缓存未命中时,会通过锁将这个缓存key锁上,等当这个请求从数据库获取数据后再回写到缓存中后,再释放锁;期间其他请求过来,会获取锁失败,等待一段时间重试,就可以直接读取缓存了。需要注意的是,如果业务量不大,进程锁就够了的话,也就没必要上分布式锁,多引入额外组件,就会增加系统的不稳定性


还可以继续改进,将请求2未获得锁,直接返回,升级成自旋锁,它不直接返回,而是等待一会重新尝试获取锁,这种高并发情况下,只有唯一请求是db请求,所有请求共享结果

  1. 给缓存的Key设置合理的过期时间并加上随机值,尽量减少缓存短期大量失效,出现大量访问数据库的情况,实现”削峰填谷”

  2. 网上有文章提出,可以让热点数据的缓存永不过期,但这其实是个很危险的操作,使用缓存的前提是一定要设置过期时间

    因为由于项目会不断迭代更新,业务不断复杂,开发人员更替,缓存会变得越来越,缓存永不过期是很危险的;我们还可以通过消息队列来间接地让热点数据的缓存延期,当热点缓存过期时,后台服务再检测更新缓存,防止缓存击穿;至于是否延期,得做访问量分析与统计,当然引入新的组件也会带来额外的稳定性问题,还是得根据业务情况,实事求是

缓存雪崩

缓存雪崩,指定是大量请求未命中缓存,直接访问数据库,导致数据库压力过大,倘若请求足够的多,会直接将数据库压垮,继而影响整个系统,如同”雪崩”

个人感觉缓存击穿是缓存雪崩的一个子集,缓存雪崩一般有2种诱因:缓存服务异常,比如redis故障宕机或者缓存服务是正常的,但大量缓存数据在同一时间过期

一般解决redis故障宕机,是搭建集群由单节点到多节点,提升redis的容灾能力,当主节点宕机后,从节点可以切换成为主节点,继续提供缓存服务;若是真的宕机了,那我们应该使用熔断机制,同时当流量到达一定的阈值,直接禁止请求对数据库的访问,返回系统拥挤之类的提示,维持系统稳定,等待缓存恢复再允许对数据库访问

防止大量缓存数据在同一时间过期,一般是给缓存的Key设置合理的过期时间并加上随机偏差,尽量让缓存失效时间均匀分布,实现”削峰填谷”,简单而有效

要么加锁,唯一db请求,所有同类请求共享结果,与缓存击穿的解决方法一致,我们就不再赘述了

还有一种方式就是,当每天系统访问的流量高峰来临之前,先提前将热点数据入缓存,避免直到用户请求的时候,再先查询数据库,然后将数据缓存的过程,这个也叫缓存预热


作者:小牛呼噜噜 ,首发于公众号小牛呼噜噜,系列文章还有:

  1. 聊聊x86计算机启动发生的事?
  2. Linux0.12内核源码解读(2)-Bootsect.S
  3. Linux0.12内核源码解读(3)-Setup.S
  4. 图解CPU的实模式与保护模式
  5. Linux0.12内核源码解读(5)-head.s
  6. Linux0.12内核源码解读(6)-main.c
  7. Linux0.12内核源码解读(7)-陷阱门初始化
  8. 图解计算机中断
  9. Linux0.12内核源码解读(9)-blk_dev_init和chr_dev_init
  10. 什么是系统调用机制?结合Linux0.12源码图解