前几天我们讲到了缓存的读写策略(缓存读写策略,我们开发人员都是这么用的)以及如何搭建高可用缓存系统(分布式缓存的高可用方案,我们都是这么做的),都是为了能在基础架构上让我们的缓存命中率能更高,防止大量的请求直接穿透我们的后端存储系统例如MySQL数据库,造成数据库的带宽和连接骤升,从而拖垮我们的整个业务。

按照互联网常理来说,我们的核心业务缓存模块命中率要达到99%,非核心业务命中率也要达到90%,当然如果是不关注缓存命中率的业务就令当再论了。

既然缓存的穿透会给我们系统带来这么大的麻烦,那我们该怎么处理并且去预防这种穿透带来的灾难呢?今天我们就来讲讲该怎么去防止缓存穿透。

什么是缓存穿透

缓存穿透最直白的意思就是,我们的业务系统在接收到请求时在缓存中并没有查到数据,从而穿透到了后端数据库里面查数据的过程。

当然,既然使用了缓存,肯定会难免有穿透的发生,正常的少量穿透是对我们业务来说是不会造成任何影响的,因为:

  • 毕竟我们的缓存容量有限,不可能去缓存所有数据,当面临较大请求时,查询到未被缓存的数据时,就会发生穿透。
  • 互联网业务的数据访问模型一般是遵循“二八”原则的,即 20% 的数据为热点数据,80% 的数据是非热点不被常访问的数据。

现在既然我们的缓存容量有限,然后 20% 的数据为热点数据,也就是说,我们可以利用有限的容量去缓存那 20% 的数据,其实就是可以保护我们的后端系统的,至于80%非热点不常用的数据发生穿透了,是我们能够接受的。

那究竟什么的缓存穿透会影响到我们的系统呢?是大量的穿透请求超过了我们后端系统的承受范围,比如恶意的穿透攻击,这样的穿透就很有可能把我们的系统给干崩溃。接下来,我们就来基于相关应用场景来解决这种缓存穿透。

缓存穿透如何解决

场景:

在我们APP的在线搜索相关系统里面,有个产品product 1 并没有在数据库中进行存储,现在通过cache aside pattern 策略查这个product 1 。

那查询一个数据库中本身就没有的数据后面会怎样呢?依照cache aside 策略,读取时,首先会读取缓存,没读到数据就会穿透到读数据库,现在数据库也没有,也就没有数据写回缓存。那么,再来个请求依然如此,更多的请求来还是一样,这样的缓存就没意义了。

通过上面场景我们可以看到,这样的系统面临非正常的穿透是会崩溃掉的,那我们该怎么去解决呢?一般我们对此有两种方案,都是有用的:

  • 设置空值
  • 布隆过滤器

设置空值

通过上面场景我们知道,当有大量恶意的穿透请求到数据库,就会给我们系统带来灾难。

所以,当我们请求数据中没有数据或者因为代码bug带来的异常造成的数据为空,这个时候我们就可以回写一个空值null到缓存中。同时,我们还要给这个null值设置过期时间,因为这个空值不具有实际业务性,而且还占用空间。

可见设置空值是可以阻挡大量穿透请求的,但是如果有大量的获取并不存在数据的穿透请求的话例如恶意攻击,则会浪费缓存空间,如果这种null值过量的话,还会淘汰掉本身缓存存在的数据,这就会使我们的缓存命中率下降。

生产建议,在使用设置空值方案时,我们要做好监控,预防缓存空间被过多null值占领造成的缓存空间浪费,如果这种数据量太大,就不再建议使用,那就使用另一种方案,即布隆过滤器。

布隆过滤器

布隆过滤器核心思想,我们把一个集合的每一个元素按照某种 hash 算法计算 hash 值,然后将hash 值对定义的数据长度取模,得到的值即为存在数组的索引。并将该索引位置值从 0 改为 1 。然后我们判断一个元素是否在这个集合的时候,只需要对这个元素计算出数组的索引值,如果这个索引位置的值为 1 则证明该元素在集合内,反之则知道不在这个集合中。

下面我们通过画个图来具体的看看布隆过滤器的工作模式,帮助大家更好的理解和应用

如上图所示,A, B, C三个元素在一个集合里面,拿到一个 D 元素,然后计算它的hash值对应于数组的位置值为 1 则表示这个 D 元素也在ABC的集合里面;接着拿到元素E 同样的计算,发现对应于数组中值为 0 则表示元素 E不在集合中。

布隆过滤器如何解决缓存穿透?

通过上面的讲解,相信大家都知道了布隆过滤器的作用了,肯定也知道怎么去用了,那回到我们今天的主角身上,下面我们就使用布隆过滤器来解决我们缓存的穿透问题。

  1. 首先我们初始化一个数组,比如长度为 20 亿。
  2. 选用一个 hash 算法,将现有产品比如上面场景的product_id 进行hash计算然后进行映射数组中。
  3. 映射到的数组值设为 1 ,其他的均为 0 。
  4. 对于新增的产品在写数据库之外,还要依照同样hash算法映射数组,更新对应位置的值。
  5. 当查询一个产品的时候,先查询这个产品是否在布隆过滤器里面,如果不在,则直接返回空给客户端,不直接穿透到数据库和缓存中。
  6. 这样就杜绝了恶意查询请求所带来的缓存穿透。

布隆过滤器性能如何?

你可能会感到疑惑,所有请求先去判断布隆过滤器,这个性能到底怎么样?

它是基于二进制数组的,数组的查询效率应该不用我多说吧,所以不管是读取还是写入,布隆过滤器的时间复杂度是O(1),即常量值。

在空间存储上,同样具有很大的优势,例如,我们20 亿的数组需要 2000000000/8/1024/1024 = 238M 的空间,而如果使用数组来存储,假设每个用户 ID 占用 4 个字节的空间,那么存储 20 亿用户需要 2000000000 * 4 / 1024 / 1024 = 7600M 的空间,是布隆过滤器的 32 倍。

布隆过滤器有什么缺陷?

纵然布隆过滤器表现的很优异,但是任何事务都不会是完美的,它也是有相应缺陷的。

  1. 由于布隆过滤器是由二进制数组和hash算法组成的,有hash算法就有hash碰撞的发生,像我们的hashmap那样(为什么你每次被问到HashMap底层原理都一知半解,搞定它),所以就有可能将并不在集合中的元素判断在里面。
  2. 布隆过滤器不支持删除元素

如何解决解决布隆过滤器的缺陷?

对于第一种缺陷,我们可以采用多个hash 算法对其计算,然后比对,多个hash映射的结果都为 1 的话,我们就判定这个元素在集合中。

对于第二个缺陷,如果不能接受的话,我们改变改变下策略,当有相同hash值时,我们就存计数值,例如,A B 相同,存的值为 2 ,不在存 bit位了,这样就会带来存储空间的消耗。所以,我们在使用时需要根据自身业务考虑,如果是不怎么删除的场景下,还是可以考虑使用布隆过滤器来解决缓存穿透问题的。

生产建议:

  • 采用多个hash 算法计算hash 值,这样可以减少误判的几率。
  • 布隆过滤器会消耗一定内存空间,根据业务场景进行评估需要多大内存,最后依据公司资源以及成本,看是否能够接受。

综上所述,回种空值和布隆过滤器是解决缓存穿透问题的两种最主要的解决方案,但是它们也有各自的适用场景,并不能解决所有问题。

总结,今天我们通过大量请求穿透到数据库中,学习了两种主要的缓存穿透方案:设置空值和布隆过滤器,所以我们解决缓存穿透问题的核心目标在于减少对于数据库的并发请求。如果今天的内容对你有帮助,或者你恰好喜欢就关注我,我会一直分享高质量技术实战内容,谢谢。

下一篇预告:互联网CDN怎么做

在公众号【架构师修炼】菜单中可自行获取专属架构视频资料,包括不限于 java架构、python系列、人工智能系列、架构系列,以及最新面试、小程序、大前端均无私奉献,你会感谢我的哈

在python中如何判断数组中的数据为空值_缓存穿透问题,开发中真实解决方案相关推荐

  1. lua中 高效判断数组(table)中是否存在某个字符 值

    lua中高效判断数组(table)中是否存在某个字符值 publish:June 28, 2016 -Tuesday by 04007 本站原创文章,转载请注明文章出处:www.04007.cn 在P ...

  2. 汇编语言 用push指令将a段中的前8个字形数据,逆序存储到b段中

    题目要求 用push指令将a段中的前8个字形数据,逆序存储到b段中 执行过程 push之前 a段数据 (b段数据)栈空间↓ 不要问我为什么不全是0,我并不知道,只是找出了一些规律.详见我的前一篇博客 ...

  3. php判断数组是否存在字符串中,php判断数组元素中是否存在某个字符串的方法

    今天在写一个函数的时候,需要用到判断数组中是否存在某个字符串,方便我们继续后面的操作,这里简单为大家介绍下,需要的朋友可以参考下 方法一:采用in_array(value,array,type) ty ...

  4. Java中高效判断数组中是否包含某个元素

    如何检查一个数组(无序)是否包含一个特定的值?这是一个在Java中经常用到的并且非常有用的操作.同时,这个问题在Stack Overflow中也是一个非常热门的问题.在投票比较高的几个答案中给出了几种 ...

  5. VBA中如何判断数组为空

    在复杂的代码开发过程中,程序逻辑结构可能存在多个分支,使用变量时,可能需要判断变量是否为空,例如对于Range变量,可以使用如下代码. Sub RangeDemo()Dim rngAll As Ran ...

  6. php判断数组的值是否为空,php中如何判断数组是否为空

    PHP判断数组为空首选方法:count($arr),size($arr); (推荐学习:PHP视频教程) 使用这个函数就好了 count 如果输出为0的话 那么就这个数组就是空的的下面就是简单的测试代 ...

  7. Matlab中分析字符串数组的文本数据

    目录 分析字符串数组的文本数据 将文本文件导入字符串数组 清理字符串数组 根据频率对单词进行排序 绘制单词频率图 用表收集基本统计信息 分析字符串数组的文本数据 下面示例演示如何以字符串数组形式存储文 ...

  8. 说说在MVC开发中,遇到的错误及解决方法(本文章是我在实际开发中总结出来的,希望对您有帮助)...

    前提:数据底层,使用的是MVC架构,对于数据表中的状态及相关字段,采用了枚举型进行对应 1 序列中不包含任何元素   解决:将Single()改为DefaultOrSingle() 2 序列包含一个以 ...

  9. java写微信小程序答辩问题_微信小程序 开发中遇到问题总结

    微信小程序 开发中遇到问题总结 1.由于小程序wx.request()方法是异步的,在app.js执行ajax后,各分页加载app.js的全局数据时,无法按顺序加载.例: //app.js App({ ...

最新文章

  1. 什么样的数据适合缓存?
  2. 近乎于“妖”!浅谈《山海镜花》的美术风格与设计
  3. 新浪微博客户端(eoe)
  4. 【SIGIR 2021 最佳学生论文】图像文本检索的动态模态交互建模
  5. JSK-A1144 代码填空:阶梯三角形【水题】
  6. 考研数学备考思路和计划制定
  7. 基于LED的室内可见光通信系统
  8. excel导出 服务器运行失败,SolidWorks 插入自制EXCEL明细表 启动服务器应用程序失败:启动excle服务器失败...
  9. unity 安卓接入科大讯飞 语音合成
  10. 项目管理知识体系指南 (五)
  11. Django之DRF自定义action
  12. Wishbone总线快速了解
  13. python 实现表情迁移
  14. 已解决SyntaxError: Non-UTF-8 code starting with ‘\xe8‘ in file
  15. 谷歌五笔输入法_输入法之争
  16. dell服务器经常自动关机,戴尔15R电脑win10系统总是自动关机?
  17. 可微神经计算机(Differentiable neural computer)的介绍
  18. poj之旅——2429,3641,1995
  19. PLC复习提纲及部分答案
  20. android 唤醒屏幕并解锁

热门文章

  1. 大数据应用的优势在哪
  2. 中琛源主要的产品是什么
  3. 报错ValueError: check_hostname requires server_hostname
  4. mybatis collection标签_MyBatis第二天(结果映射+动态sql+关联查询)
  5. vue组件制作专题 - (mpvue专用)在mpvue中纯自己写css实现简单左右轮播
  6. matlab转变图像位深,[转载]matlab 图像处理命令 (转)
  7. ln -s命令 linux_浅谈linux中ln命令,附带案例
  8. java 栈_Java实现单链表、栈、队列三种数据结构
  9. 2021年SWPUACM暑假集训day5单调栈算法
  10. employees mysql_「employees」mysql示例employees数据库 - seo实验室