大萧条时期的野蛮生长——缓存穿透的解决思路

399 阅读4分钟

一起养成写作习惯!这是我参与「掘金日新计划·4月更文挑战」的第8天,点击查看活动详情

缓存在现代互联网服务中被广泛运用的技术,从CPU到上层软件,几乎无处不在,无时无刻不在为用户提供更快的访问速度。其实道理也和简单,数据库本身是脆弱的,当用户量达到一定规模之后,数据库的读写瓶颈会来的非常早,极大限制了服务能力的上限。所以要在数据库之上再加一层可以提供高速访问的存储层,兵来将挡水来土掩。这里不像CPU那样会设置多级缓存,软件服务更多的是去水平扩展缓存的集群,保证稳定性和性能,对一致性的要求稍弱一点。

今天需要探讨的问题是:缓存穿透怎么办?我们使用缓存的目的是保护数据库,如果穿透那就对数据库失去了保护作用,对于一个亿级别的服务,Miss掉1%的请求就会给数据库带来1万的额外请求量,这是十分危险的量了。所以我们需要设计一些方法来拦截这些可能的穿透请求。

空值

如果因为缓存过期导致的穿透并不在这里的讨论范围内,因为并不是所有的key都会同一时间过期,其次过期之后穿透到数据库一次便可以再次设置缓存,用以应对后续的请求。所以这样的穿透对于绝大多数服务的影响都是可以承受的。这里要说的是数据库中本来就没有的值在反复请求,这样永远会穿透缓存,然后数据库的查询性能也会被白白占用。

这里有一个可以止损的方法是,当数据库也未查到时可以把当前的key在缓存中设置空值,并且设置一个较短的过期时间。这样短时间大量的无效请求便可以被有效拦截。这种方法的缺点在于缓存存储的压力会比较大,如果收到爬虫的攻击,可能很快缓存就用完了。所以这个方案只能轻度使用,还需要搭配其他更复杂的方案一起使用。

布隆过滤器

写空值这样的手段可能会浪费大量存储,将缓存写满是一个更加危险的事,为了避免这样的事,就应该在查缓存前知道这条数据有没有,对于一些不存在的数据直接拦截就好了。这层保护的设计,第一次是历史上有个人叫布隆的人提出的,所以叫这个名字。下面简单画一下这个过滤器的原理:

image.png

image.png

(图片出处如何抗住亿级流量之布隆过滤器

如图所示,每一条数据请求时会经历多次hash计算,每次计算都会得出数组的下标,并将对应的下标设置为1,其他位默认为0。这就构成了一个以0和1组成的数组,这个数组就是布隆的本体了。布隆过滤器有以下特点:

  1. 数据对应的位存在0时,该数据一定没有
  2. 数据位全为1时,该数据可能是不存在的
  3. 不支持删除和更新操作,只能新增

当数据经过hash计算后,在数组的所有位都是1时,说明该数据确实是存在的,可以放行去下级缓存去查询。但如果hash算法不够散,会存在碰撞的可能,所以也不一定能保证100%的拦截,存在误判的可能。但如果任意一位有0,则一定是无效查询。因为每个数据都会hash成多位,所以单一一位数据可能被多个数据引用,所以这个数组一定不能删除或者更新,否则就失去拦截的效果了。