缓存穿透、击穿、雪崩

概述

        用户的数据一般都是存储在数据库中,也就是硬盘上,但硬盘的读写速度是计算机里最慢的硬件了。当用户请求都访问数据库,访问量一上来,数据库很容易就崩溃了,所以为了避免用户直接访问数据库,会用redis作为缓存层。

        redis作为内存数据库,内存的读写速度比硬盘高出好几个数量级,大大提高了系统性能。

        但引入了缓存层就会有缓存异常的三个问题:缓存穿透、缓存击穿和缓存雪崩。

缓存穿透

        当用户访问一个不存在的数据时,导致请求在访问缓存的时候发现缓存缺失,再去访问数据库时,发现数据库中也没有该数据,数据库没办法写入缓存来服务后续请求。当大量这样的请求到来时,数据库压力骤增,这就是缓存穿透。

常见场景:

        业务误操作,缓存和数据库中的数据都被误删掉了,或者有攻击者伪造了大量请求,请求某个不存在的数据(比如查询Id=0 或者Id=负数)。就会造成:

  • 缓存里没有对应的数据,所以查询会落到数据库上。
  • 数据库也没有数据,所以没有办法回写缓存,下次同样的请求还是会落到数据库上。

解决方法:

1.返回特殊值(如NULL):

        在缓存未命中且数据库中也不存在时,往缓存中写入一个特殊值,当下一次请求过来时看到这个特殊值就没有必要继续查询数据库了。但如果攻击者每次使用不同的Key来访问时,就会造成回写大量特殊值,造成redis内存浪费,进而引起另一个问题,也就是redis内存不足,可能会导致执行内存淘汰时把其它有用的数据淘汰掉。

优点:简单。

缺点:浪费内存。

2.布隆过滤器

        布隆过滤器是一个快速判断元素是否存在的结构,它可以在很小的内存占用下,快速判断一个元素是否在一个集合中。当一个访问过来时先进行判断是否存在,若不存在则直接返回,避免对数据库的查询。

优点:内存占用较少,没有多余 key
缺点:实现复杂,存在误判

3.限流策略

        当有大量恶意请求访问不存在的数据时,在 API入口处我们要判断求请求参数是否合理,请求参数是否含有非法值、请求字段是否存在,如果判断出是恶意请求就直接返回错误,避免进一步访问缓存和数据库。

布隆过滤器

        布隆过滤器由初始值都为0的位图数组N个哈希函数两部分组成。在写入数据库数据时,在布隆过滤器里做个标记,这样下次查询数据是否在数据库时,只需要查询布隆过滤器,如果查询到数据没有被标记,说明不在数据库中。

  • 存储数据: id 为 1 的数据,通过多个 hash 函数获取 hash 值,根据 hash 计算数组对应位置改为 1
  • 查询数据:使用相同 hash 函数获取 hash 值,判断对应位置是否都为 1

        但布隆过滤器由于是基于哈希函数实现査找的,高效査找的同时存在哈希冲突的可能性,比如数据x和数据y 可能都落相同位置上,存在误判的情况。所以,查询布降过滤器说数据存在,并不一定证明数据库中存在这个数据,但是查询到数据不存在,数据库中一定就不存在这个数据。

缓存击穿

        如果缓存中的某个热点数据过期了,此时大量的请求访问了该热点数据,就无法从缓存中读取,直接访问数据库,数据库很容易就被高并发的请求冲垮,这就是缓存击穿的问题。

解决方案

  • 互斥锁:当缓存失效时,不立即去load db,先使用如 Redis 的 setnx 去设置一个互斥锁,当操作成功返回时再进行load db的操作并回设缓存,否则重试get缓存的方法。
  • 逻辑过期:在设置key的时候,设置一个过期时间字段一块存入缓存中,不给当前key设置过期时间当查询的时候,从redis取出数据后判断时间是否过期,如果过期则开通另外一个线程进行数据同步,当前线程正常返回数据,这个数据不是最新的。

当然两种方案各有利弊:

  • 如果选择数据的强一致性,建议使用分布式锁的方案,性能上可能没那么高,锁需要等,也有可能产生死锁的问题
  • 如果选择key的逻辑删除,则优先考虑的高可用性,性能比较高,但是数据同步这块做不到强一致。


缓存雪崩

        缓存雪崩一般有两个常见场景:当大量缓存数据在同一时间过期(失效)或者Redis 故障宕机时。

        如果此时有大量的用户请求,都无法在 Redis 中处理,于是全部请求都直接访问数据库,从而导致数据库的压力骤增,严重的会造成数据库宕机,从而形成一系列连锁反应,造成整个系统崩溃,这就是缓存雪崩的问题。

解决方案

大量数据同时过期的解决方法:

  • 给不同的 Key 的 过期时间添加随机值。
  • 互斥锁。
  • 数据预热

redis故障宕机的解决方法:

  • 利用 Redis 集群提高服务的可用性:通过主从节点的方式构建 Redis 缓存高可靠集群,如果 Redis 缓存的主节点故障宕机,从节点可以切换成为主节点,继续提供缓存服务,避免了由于 Redis 故障宕机而导致的缓存雪崩问题。
  • 给业务添加多级缓存。
  • 给缓存业务添加降级限流策略。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值