大家好,我是楼仔!
今天写的这个主题内容,其实非常基础,但是作为高并发非常重要的几个场景,绝对绕不开,估计大家面试时,也经常会遇到。
这个主题的文章,网上非常多,本来想直接转载一篇,但是感觉没有合适的,要么文章不够精炼,要么就是精简过头,所以还是自己写一篇吧。
内容虽然基础,但我还是秉承以往的写作风格,参考众多优秀的博客后,打算写一篇能通俗易懂,又不失全面的文章。
前言
我们先看一下正常情况的查询过程:
- 先查询 Redis,如果查询成功,直接返回,查询不存在,去查询 DB;
- 如果 DB 查询成功,数据回写 Redis,查询不存在,直接返回。
缓存穿透
定义:当查询数据库和缓存都无数据时,因为数据库查询无数据,出于容错考虑,不会将结果保存到缓存中,因此每次请求都会去查询数据库,这种情况就叫做缓存穿透。
红色的线条,就是缓存穿透的场景,当查询的 Key 在缓存和 DB 中都不存在时,就会出现这种情况。
可以想象一下,比如有个接口需要查询商品信息,如果有恶意用户模拟不存在的商品 ID 发起请求,瞬间并发量很高,估计你的 DB 会直接挂掉。
可能大家第一反应就是对入参进行正则校验,过滤掉无效请求,对!这个没错,那有没有其它更好的方案呢?
缓存空值
当我们从数据库中查询到空值时,我们可以向缓存中回种一个空值,为了避免缓存被长时间占用,需要给这个空值加一个比较短的过期时间,例如 3~5 分钟。
不过这个方案有个问题,当大量无效请求穿透过来时,缓存内就会有有大量的空值缓存,如果缓存空间被占满了,还会因剔除掉一些已经被缓存的用户信息,反而会造成缓存命中率的下降,所以这个方案,需要评估缓存容量。
如果缓存空值不可取,这时你可以考虑使用布隆过滤器。
布隆过滤器
布隆过滤器是由一个可变长度为 N 的二进制数组与一组数量可变 M 的哈希函数构成,说的简单粗暴一点,就是一个 Hash Map。
原理相当简单:比如元素 key=#3,假如通过 Hash 算法得到一个为 9 的值,就存在这个 Hash Map 的第 9 位元素中,通过标记 1 标识该位已经有数据,如下图所示,0 是无数据,1 是有数据。
所以通过该方法,会得到一个结论:在 Hash Map 中,标记的数据,不一定存在,但是没有标记的数据,肯定不存在。
为什么“标记的数据,不一定存在”呢?因为 Hash 冲突!
比如 Hash Map 的
回复