logo头像

待到风起时,扬帆济沧海

缓存穿透、雪崩、击穿的解决方法

本文于365天之前发表,文中内容可能已经过时。

1.缓存穿透

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

1.1解决办法

  • 将不存在的key设置默认的值

    如果有人利用ID攻击应用,可以将这个key预先设置一个null或者程序可判断的值,决定应用是否进行下面的执行。当缓存失效或者缓存key经过轮训以后不再为空,则进行程序的后续执行

  • 布隆过滤器

    简单的说,bloom算法类似一个hash set,用来判断某个元素的key是否存在集合中。和一般的hash set不同的是,这个算法无需存储key的值,值需要k个比特位,每个存在一个标志,用来判断key是否存在集合中。
    算法:

    1. 首先需要k个hasn函数,每个函数可以把key散列成为1个整数
    2. 初始化时,需要一个长度为n比特的数组,每个比特位初始化为0
    3. 某个key加入集合时,用k个hash函数计算出k个散列值,并把数组中对应的比特位设置为1
    4. 判断某个key是否在集合时,用k个hash函数计算出k个散列值,并查询数组中对应的比特位,如果所有的比特位都是1,则认为在集合中

      优点:不需要存储key,节省空间

      缺点:

    5. 算法判断key在集合时,有一定的概率key其实不在集合中
    6. 无法删除

2.缓存雪崩

大量的key设置了相同的过期时间,导致在缓存在同一时刻失效,造成DB的请求大、压力大,引起雪崩

2.1解决办法

从业务层面。可以给缓存设置过期时间上加上一个随机值,使得每个key的过期时间分布开来,不会集中在同一时刻

3. 缓存击穿(并发)

一个存在的key,在缓存过期的一刻,同时有大量的请求,这些请求都会击穿到DB,造成瞬时DB请求量大、压力骤增。

3.1解决办法

  1. 使用互斥锁:让一个线程构建缓存,其他线程等待等待构建缓存的线程执行完,从新从缓存中获取数据就行(如下图)
    mutex-key-pic
    如果是单机,可以用synchronize或者lock来处理,如果是分布式环境可以使用分布式锁(memcached的add、redis的setnx,zookeeper的添加节点),当缓存构建完成以后释放分布式锁
  2. 后台刷新:定义个Job专门主动刷新缓存,比如缓存30分钟,那么Job可以设置每隔29分钟定时将DB数据刷新缓存中。这种方案适合key相对固定,cache粒度较大
  3. 检查更新:将缓存的过期时间(绝对时间)一起保存到缓存中(可以是key拼接,或者value中存放字段),在每次只需get操作以后都将get出来的缓存与当前系统时间进行比较,缓存时间-当前时间<=1分钟(自定义时间阈值),则主动更新缓存

4. 如何解决缓存单机热点问题

分布式缓存一致性hash算法解决


简单概述:

  1. 一致性hash算法通过构造一个长度为2^32的整数环
  2. 根据节点名(或者服务器IP等信息)的hash值将缓存服务器节点放置在这个环上
  3. 计算需要缓存的key的hash值,顺时针找到最近的的服务器节点,将数据存放在该节点上

评论系统未开启,无法评论!