知识卡片

缓存穿透:两种成因及应对

结构图卡

内容

缓存的价值在于弥补存储系统在两类场景下的不足:需要复杂运算才能得出的数据(如实时统计在线人数,用MySQL做count(*)无论怎么优化性能都不会太高)、以及读多写少的数据(如一条微博被几千万人浏览,写只有一次insert,读却是几千万次select)——缓存把这类可复用数据放进内存,一次生成多次使用,从而大幅减轻存储系统压力。但缓存本身也会引入新的复杂性,”缓存穿透”是其中之一:业务系统查缓存却没查到数据,只好再回存储系统查一遍,缓存没有起到分担压力的作用,常见有两种成因。第一种是被访问的数据本身在存储系统里就不存在——通常存储系统没有的数据也不会被写进缓存,导致每次查询这类不存在的数据都要穿透到存储系统一趟,正常情况下这类请求量不大,但一旦被黑客盯上、故意大量访问某些确定不存在的数据,就有可能把存储系统直接拖垮;解法很简单,只要存储系统查不到数据,就在缓存里存一个默认值(可以是空值也可以是具体的兜底值),下次读缓存就能直接拿到默认值,不用再穿透到存储系统。第二种是数据本身存在,但生成对应的缓存数据本身很耗时或耗资源,一旦刚好在业务访问时这份缓存失效了,压力就会集中砸向存储系统——典型场景是电商商品分页,因为数据量巨大只能按分页缓存,正常用户访问集中在前几页、后面的分页缓存容易过期,一旦被竞争对手的爬虫从头到尾遍历一遍,大量失效的分页缓存会集中触发耗性能的数据库查询(如order by limit操作),把整个数据库拖慢;这种情况没有太好的根治方案,因为爬虫什么时候来、遍历多深都不确定,常见的应对只能是识别并禁止爬虫访问(代价是可能影响SEO和正常推广)、或者做好监控,靠爬虫攻击本身是渐进式而非瞬时暴力破坏这一点争取处理时间。

结构图

flowchart TB
  A["缓存穿透:查缓存没命中,穿透到存储系统"]
  A --> B["成因①数据确实不存在<br/>缓存中永远没有对应数据"]
  B --> B1["风险:被黑客批量访问不存在数据<br/>可能拖垮存储系统"]
  B1 --> B2["解法:查不到也在缓存写入默认值<br/>下次直接命中默认值"]
  A --> C["成因②数据存在,但生成缓存耗时/耗资源<br/>访问时恰好缓存失效"]
  C --> C1["典型:电商分页缓存被爬虫从头到尾遍历<br/>大量深页缓存集中失效"]
  C1 --> C2["无根治方案:识别并禁止爬虫(可能伤SEO)<br/>或做好监控争取处理时间"]

参考来源

- 位置:《从零开始学架构》第17讲《高性能缓存架构》"缓存穿透"(源文件:_epub-src/OEBPS/text00001.html) - 结论依据:原文说明"如果存储系统中没有某个数据,则不会在缓存中存储相应的数据……如果查询存储系统的数据没有找到,则直接设置一个默认值……存到缓存中",并以电商分页缓存被爬虫遍历为例说明"这种情况并没有太好的解决方案……通常的应对方案要么就是识别爬虫然后禁止访问……要么就是做好监控",直接支撑本卡片结论与结构图。 - 原始内容:缓存穿透是指缓存没有发挥作用,业务系统虽然去缓存查询数据,但缓存中没有数据……如果查询存储系统的数据没有找到,则直接设置一个默认值……存到缓存中……这种情况并没有太好的解决方案。