当前位置:首页>科技>缓存容量预估5W1H分析法帮你系统掌握缓存
发布时间:2026-07-21阅读(1)

来,先上文章的目录,让大家可以对 缓存 这块知识先建立一个系统性的认知,然后我会按点逐个击破,读者们也可以按需阅读哈!

文章目录
一、什么是缓存(What)维基百科对缓存的定义是:
In computing, a cache is a hardware or software component that stores data so that future requests for that data can be served faster; the data stored in a cache might be the result of an earlier computation or a copy of data stored elsewhere.
简而言之,缓存就是存储数据副本或计算结果的组件,以便后续可以更快地访问。

有缓存 VS 无缓存
在计算中,缓存是一个高速数据存储层,其中存储了数据的子集,且通常是短暂性存储,这样日后再次请求该数据时,直接读缓存会比重新计算结果或读数据存储更快。通过缓存,你可以高效地重用之前检索或计算的数据。
二、为什么要使用缓存(Why)从定义上可以看出所谓缓存其实是其他数据的副本,使用缓存是为了更快地检索或计算数据。
1.硬件层面:如CPU中的高速缓存
CPU VS 内存
从上图可以看出CPU(蓝色)和内存(粉红色)之间存在巨大的性能差距。
在CPU访问数据和指令遵循计算机系统的局部性原理:
使用高速缓存可以弥补CPU和内存之间的性能差异,减少 CPU 浪费计算时间等待内存数据。
2.软件层面是否可以解决用户的痛点问题决定着用户会不会一开始尝试使用某款产品,是否有极致的用户体验影响用户会不会持续使用某款产品。在业务中使用缓存的目的就是通过扩大系统吞吐量、减少时延和响应时间来优化用户体验,适当的性能优化可以提升体验,增强用户粘性。
在现有的互联网应用中,缓存的使用是一种能够提升服务快速响应的关键技术,也是产品经理无暇顾及的非功能需求,需要在设计技术方案时对业务场景,具有一定的前瞻性评估后,决定在技术架构中是否需要引入缓存解决这种这种非功能需求。
三、什么时候使用缓存(When)缓存不是架构设计的必选项,也不是业务开发中的必要功能点,只有在业务出现性能瓶颈,进行优化性能的时候才需要考虑使用缓存来提升系统性能。并非所有的业务场景都适合用缓存,读多写少、不要求一致性、时效要求越低、访问频率越高、对最终一致性和数据丢失有一定程度的容忍的场景才适合使用缓存,缓存并不能解决所有的性能问题,倘若滥用缓存会带来额外的维护成本,使得系统架构更复杂更难以维护。
虽然缓存适用于各种各样的案例,但要充分利用缓存,需要进行一定的规划。所以在决定是否缓存一段数据时,请考虑以下问题:
另外把缓存当做存储来使用是一件极其致命的做法,这种错误的认识,将缓存引入系统的那一刻起就意味着已经让系统走上了危险的局面,只有对缓存的使用边界有深刻的理解,才能尽可能减少引入缓存带来的副作用。
四、谁会使用缓存(Who)其实缓存的思想随处可见,日常生活中人们都会有意无意地用到。比如你会把常看的书放到书桌上,这样你可以更快地拿到它们,而受限于桌面空间,不常看的书就要放在空间更大的书柜里了,等到要看的时候再从书柜里拿出来放到书桌上。这里的书桌其实就是一种缓存介质了。
对于程序员来说,缓存更是家常便饭了。有哪些程序员会用到缓存技术呢?
1.硬件开发工程师:比如CPU缓存、GPU缓存和数字信号处理器(DSP)缓存等。
2.软件开发工程师:
按照不同的维度,可以对缓存分门别类,如下图所示。

缓存分类
下面我着重按照 缓存所处链路节点的位置 来系统梳理出不同类型的缓存应用。
(1)客户端缓存HTTP 协议的无状态性决定了它必须依靠客户端缓存来解决网络传输效率上的缺陷。由于每次请求都是独立的,服务端不保存此前请求的状态和资源,所以也不可避免地导致其携带有重复的数据,造成网络性能降低。HTTP 协议对此问题的解决方案便是客户端缓存。
常见的客户端缓存有如下几种:
1)页面缓存
页面缓存是指将静态页面获取页面中的部分元素缓存到本地,以便下次请求不需要重复资源文件,h5很好的支持的离线缓存的功能,具体实现可通过页面指定manifest文件,当浏览器访问一个带有manifest属性的文件时,会先从应用缓存中获取加载页面的资源文件,并通过检查机制处理缓存更新的问题。
2)APP缓存
APP可以将内容缓存到内存或者本地数据库中,例如在一些开源的图片库中都具备缓存的技术特性,当图片等资源文件从远程服务器获取后会进行缓存,以便下一次不再进行重复请求,并可以减少用户的流量费用。
客户端缓存是前端性能优化的一个重要方向,毕竟客户端是距离“用户”最近的地方,是一个可以充分挖掘优化潜力的地方。
3)浏览器缓存
浏览器缓存通常会专门开辟内存空间以存储资源副本,当用户后退或者返回上一步操作时可以通过浏览器缓存快速的获取数据,减少页面加载时间和带宽使用。在 HTTP 从 1.0 到 1.1,再到 2.0 版本的每次演进中,逐步形成了现在被称为“状态缓存”、“强制缓存”(许多资料中简称为“强缓存”)和“协商缓存”的 HTTP 缓存机制。在HTTP 1.1中通过引入e-tag标签并结合expire、cache-control两个特性能够很好的支持浏览器缓存。
下面我们用思维导图理解 强制缓存 和 协商缓存,请注意体会梳理思路!

浏览器缓存
对于Etag的补充说明:
HTTP 服务器可以根据自己的意愿来选择如何生成这个标识,比如 Apache 服务器的 Etag 值默认是对文件的索引节点(INode),大小和最后修改时间进行哈希计算后得到的。
Etag 是 HTTP 中一致性最强的缓存机制,比如,Last-Modified 标注的最后修改只能精确到秒级,如果某些文件在 1 秒钟以内,被修改多次的话,它将不能准确标注文件的修改时间;又或者如果某些文件会被定期生成,可能内容并没有任何变化,但 Last-Modified 却改变了,导致文件无法有效使用缓存,这些情况 Last-Modified 都有可能产生资源一致性问题,只能使用 Etag 解决。
Etag 却又是 HTTP 中性能最差的缓存机制,体现在每次请求时,服务端都必须对资源进行哈希计算,这比起简单获取一下修改时间,开销要大了很多。Etag 和 Last-Modified 是允许一起使用的,服务器会优先验证 Etag,在 Etag 一致的情况下,再去对比 Last-Modified,这是为了防止有一些 HTTP 服务器未将文件修改日期纳入哈希范围内。
扩展知识:内容协商机制
到这里为止,HTTP 的协商缓存机制已经能很好地处理通过 URL 获取单个资源的场景,为什么要强调“单个资源”呢?在 HTTP 协议的设计中,一个 URL 地址是有可能能够提供多份不同版本的资源,比如,一段文字的不同语言版本,一个文件的不同编码格式版本,一份数据的不同压缩方式版本,等等。因此针对请求的缓存机制,也必须能够提供对应的支持。为此,HTTP 协议设计了以 Accept(Accept、Accept-Language、Accept-Charset、Accept-Encoding)开头的一套请求 Header 和对应的以 Content-(Content-Language、Content-Type、Content-Encoding)开头的响应 Header,这些 Headers 被称为 HTTP 的内容协商机制。与之对应的,对于一个 URL 能够获取多个资源的场景中,缓存也同样也需要有明确的标识来获知根据什么内容来对同一个 URL 返回给用户正确的资源。这个就是 Vary Header 的作用,Vary 后面应该跟随一组其他 Header 的名字,比如:
复制1HTTP/1.1 200 OK2Vary: Accept, User-Agent1.2.
以上响应的含义是应该根据 MIME 类型和浏览器类型来缓存资源,获取资源时也需要根据请求 Header 中对应的字段来筛选出适合的资源版本。
(2)网络缓存网络缓存位于客户端以及服务端中间,通过代理的方式解决数据请求的响应,降低数据请求的回源率。回源率又分为以下两种:
网络缓存常见的代理形式分为两种:web代理缓存、边缘缓存。
在介绍网络缓存之前我们先了解下前置知识----两种服务器代理方式:正向代理、反向代理。
正向代理其实就是:客户端通过代理服务器与源服务器进行非直接连接。客户端可以感知到代理服务器的存在,对源服务器透明(源服务器感知不到客户端的存在),如下图所示:

正向代理
通信时客户端和代理服务器要设置好代理协议,比如Socks协议或者是HTTP协议(可以设置4.1节浏览器缓存中的HTTP Header)。
那正向代理有什么作用呢?
反向代理就是:客户端通过代理服务器与源服务器进行非直接连接。客户端只会得知反向代理的IP地址,而不知道在代理服务器后面的服务器集群的存在。如下图所示:

反向代理
反向代理有什么作用呢?
1)web代理缓存:web代理缓存通常是指正向代理,会将资源文件和热点数据放在代理服务器上,当新的请求到来时,如果在代理服务器上能获取数据,则不需要重复请求到应用服务器上。
2)边缘缓存:和正向代理一样,反向代理同样可以用于缓存,例如nginx就提供了缓存的功能。进一步,如果这些反向代理服务器能够做到和用户请求来自同一个网络,那么获取资源的速度进一步提升,这类的反向代理服务器可以称之为边缘缓存。常见的边缘缓存就是内容分发网络(Content Delivery Network),简称CDN。可以将图片等静态资源文件放到CDN上。
那什么CDN呢?
如果把某个互联网系统比喻为一家跨国企业,那内容分发网络就是它遍布世界各地的分销机构,如果现在有客户要买一块 CPU,那订机票飞到美国加州英特尔总部肯定是不合适的,到本地电脑城找个装机店铺才是普遍的做法,在此场景中,内容分发网络就相当于电脑城里的本地经销商。
那CDN的主要工作过程有哪些呢?主要包括路由解析、内容分发、负载均衡(干货文章,建议点击阅读)、CDN的利用场景。
路由解析一次没有内容分发网络参与的DNS域名解析过程如下:
无论是使用浏览器抑或是在程序代码中访问某个网址域名,比如以www.wallbig.club.cn为例,如果没有缓存的话,都会先经过 DNS 服务器的解析翻译,找到域名对应的 IP 地址才能开始通信,这项操作是操作系统自动完成的,一般不需要用户程序的介入。不过,DNS 服务器并不是一次性地将“www.wallbig.club.cn”直接解析成 IP 地址,需要经历一个递归的过程。首先 DNS 会将域名还原为“www.wallbig.club.cn.”,注意最后多了一个点“.”,它是“.root”的含义。早期的域名必须带有这个点才能被 DNS 正确解析,如今几乎所有的操作系统、DNS 服务器都可以自动补上结尾的点号,然后开始如下解析步骤:
权威域名服务器(Authoritative DNS):是指负责翻译特定域名的 DNS 服务器,“权威”意味着这个域名应该翻译出怎样的结果是由它来决定的。DNS 翻译域名时无需像查电话本一样刻板地一对一翻译,根据来访机器、网络链路、服务内容等各种信息,可以玩出很多花样,权威 DNS 的也有很多灵活应用,后面也会讲到。
根域名服务器(Root DNS)是指固定的、无需查询的顶级域名(Top-Level Domain)服务器,可以默认为它们已内置在操作系统代码之中。全世界一共有 13 组根域名服务器(注意并不是 13 台,每一组根域名都通过任播的方式建立了一大群镜像,根据维基百科的数据,迄今已经超过 1000 台根域名服务器的镜像了)。13 这个数字是由于 DNS 主要采用 UDP 传输协议(在需要稳定性保证的时候也可以采用 TCP)来进行数据交换,未分片的 UDP 数据包在 IPv4 下最大有效值为 512 字节,最多可以存放 13 组地址记录,由此而来的限制。
现在假设本地 DNS 是全新的,上面不存在任何域名的权威服务器记录,所以当 DNS 查询请求按步骤 3 的顺序一直查到根域名服务器之后,它将会得到“cn的权威服务器”的地址记录,然后通过“cn的权威服务器”,得到“club.cn的权威服务器”的地址记录,以此类推,最后找到能够解释www.wallbig.club.cn的权威服务器地址。
通过“www.wallbig.club.cn的权威服务器”,查询www.wallbig.club.cn的地址记录,地址记录并不一定就是指 IP 地址,在 RFC 规范中有定义的地址记录类型已经多达数十种,比如 IPv4 下的 IP 地址为 A 记录,IPv6 下的 AAAA 记录、主机别名 CNAME 记录,等等。
下面画个图,方便大家理解这个解析过程:

没有CDN参与的用户访问解析过程
前面提到过,每种记录类型中还可以包括多条记录,以一个域名下配置多条不同的 A 记录为例,此时权威服务器可以根据自己的策略来进行选择,典型的应用是智能线路:根据访问者所处的不同地区(比如华北、华南、东北)、不同服务商(比如电信、联通、移动)等因素来确定返回最合适的 A 记录,将访问者路由到最合适的数据中心,达到智能加速的目的。
那如果有CDN参与的话,路由解析的具体工作过程又是哪样的呢?
为了读者更生动地理解以上步骤,我画个时序图,建议和上面的图对比来看:

CDN路由解析过程
DNS 系统多级分流的设计使得 DNS 系统能够经受住全球网络流量不间断的冲击,但也并非全无缺点。典型的问题是响应速度,当极端情况(各级服务器均无缓存)下的域名解析可能导致每个域名都必须递归多次才能查询到结果,显著影响传输的响应速度。
专门有一种被称为“DNS 预取”(DNS Prefetching)的前端优化手段用来避免这类问题:如果网站后续要使用来自于其他域的资源,那就在网页加载时生成一个 link 请求,促使浏览器提前对该域名进行预解释,比如下面代码所示:
复制<link rel="dns-prefetch" href="//domain.not-wallbig.club">1.
而另一种可能更严重的缺陷是 DNS 的分级查询意味着每一级都有可能受到中间人攻击的威胁,产生被劫持的风险。要攻陷位于递归链条顶层的(比如根域名服务器,cn 权威服务器)服务器和链路是非常困难的,它们都有很专业的安全防护措施。但很多位于递归链底层或者来自本地运营商的 Local DNS 服务器的安全防护则相对松懈,甚至不少地区的运营商自己就会主动进行劫持,专门返回一个错的 IP,通过在这个 IP 上代理用户请求,以便给特定类型的资源(主要是 HTML)注入广告,以此牟利。
为此,最近几年出现了另一种新的 DNS 工作模式:HTTPDNS(也称为 DNS over HTTPS,DoH)。它将原本的 DNS 解析服务开放为一个基于 HTTPS 协议的查询服务,替代基于 UDP 传输协议的 DNS 域名解析,通过程序代替操作系统直接从权威 DNS 或者可靠的 Local DNS 获取解析数据,从而绕过传统 Local DNS。这种做法的好处是完全免去了“中间商赚差价”的环节,不再惧怕底层的域名劫持,能够有效避免 Local DNS 不可靠导致的域名生效缓慢、来源 IP 不准确、产生的智能线路切换错误等问题。
内容分发在 DNS 服务器的协助下,无论是对用户还是服务器,内容分发网络都可以是完全透明的,在两者都不知情的情况下,由 CDN 的缓存节点接管了用户向服务器发出的资源请求。后面随之而来的问题是缓存节点中必须有用户想要请求的资源副本,才可能代替源站来响应用户请求。这里面又包括了两个子问题:“如何获取源站资源”和“如何管理(更新)资源”。
CDN 获取源站资源的过程被称为“内容分发”,“内容分发网络”的名字正是由此而来,可见这是 CDN 的核心价值。目前主要有以下两种主流的内容分发方式:
对于“CDN 如何管理(更新)资源”这个问题,同样没有统一的标准可言,尽管在 HTTP 协议中,关于缓存的 Header 定义中确实是有对 CDN 这类共享缓存的一些指引性参数,比如“浏览器”小节HTTP header参数Cache-Control的 s-maxage,但是否要遵循,完全取决于 CDN 本身的实现策略。
现在,最常见的做法是超时被动失效与手工主动失效相结合。超时失效是指给予缓存资源一定的生存期,超过了生存期就在下次请求时重新被动回源一次。而手工失效是指 CDN 服务商一般会提供给程序调用来失效缓存的接口,在网站更新时,由持续集成的流水线自动调用该接口来实现缓存更新。
负载均衡负载均衡就是以统一的接口对外提供服务,但构建和调度服务集群对用户保持透明。深入了解负载均衡强烈推荐看这篇干货:老生常谈的负载均衡,你真的懂了吗?
CDN的应用场景缓存击穿是一个失效的热点Key被并发集中访问,导致请求全部打在数据库上。图如下:

缓存击穿
解决方案:(1)热点key缓存不失效:对热点key可以设置永不过期。
(2)使用互斥锁或堵塞队列:这样可以控制数据库的线程访问数,减小数据库的压力,但也会让系统吞吐率有所下降。实现流程如下:
(3)用堵塞队列来实现的话可以参考Golang官方库singleflight
热点数据由代码来手动管理,缓存击穿是仅针对热点数据被自动失效才引发的问题,对于这类数据,可以直接由开发者通过代码来有计划地完成更新、失效,避免由缓存策略来自动管理。
2.缓存雪崩某个时刻热点数据出现大规模的缓存失效,大量的请求全部打到数据库,导致数据库瞬时压力过载从而拒绝服务甚至是宕机。

缓存雪崩
分析:造成缓存雪崩的关键在于在同一时间大规模的key失效。诱因可能是:
(1)大量热点数据设置了相同或相近的过期时间。
(2)缓存组件不可用,比如redis宕机了。
解决方案:(1)打散缓存失效时间:主要是通过对key的TTL增加随机数去尽量规避,过期时间则需要根据业务场景去设置。
(2)使用多级缓存:这里有个github开源库:hybridcache可以借鉴下
(3)兜底逻辑使用熔断机制。防止过多请求同时打到DB。
在触发熔断机制后返回预先配置好的兜底数据,减少过多请求压倒DB导致服务不可用。

熔断检测 数据兜底
(4)组件高可用:

redis哨兵模式
Redis基于一个Master主节点多Slave从节点的模式和Redis持久化机制,将一份数据保持在多个实例中,从而实现增加副本冗余量,又使用哨兵机制实现主备切换, 在master故障时,自动检测,将某个slave切换为master,最终实现Redis高可用
缓存穿透是指用户查询数据库没有的数据,缓存中自然也不会有。那先查缓存再查数据相当于进行了两次无效操作。大量的无效请求将给数据库带来极大的访问压力,甚至导致其过载拒绝服务。下图中红色箭头标出了每一次用户请求到来都会经过的两次无效查询。

缓存穿透
分析:造成缓存穿透的原因可能是:
(1)空数据查询(黑客攻击),空数据查询通常指攻击者伪造大量不存在的数据进行访问(比如不存在的商品信息、用户信息)
(2)缓存污染(网络爬虫),缓存污染通常指在遍历数据等情况下冷数据把热数据驱逐出内存,导致缓存了大量冷数据而热数据被驱逐。
解决方案:(1)对于空数据查询:
1)使用布隆过滤器高效判断key是否存在,流程如如下:

布隆过滤器方案
虽然不能完全避免数据穿透的现象,但已经可以将99%的穿透查询给屏蔽在Redis层了,极大的降低了底层数据库的压力,减少了资源浪费(布隆过滤器用bitmap实现)。如果想拥有更高的空间利用率和更小的误判,替代的方案是使用布谷鸟过滤器。如果想了解这两种过滤器的相关细节,强烈推荐看这篇文章:作为一名后台开发,你必须知道的两种过滤器
由于布隆过滤器存在“误报”和“漏报”,而且实现也比较复杂,对于空数据查询其实还有一种简单粗暴的策略:
2)缓存空值,流程图如下:

当碰到查询结果为空的key时,放一个空值到缓存中,下次再访问就知道此key是无效的,避免无效查询数据库。但这样花费额外的空间来存储空值。
(2)对于缓存污染:关键点是能识别出只访问一次或者访问次数很少的数据。然后使用淘汰策略去删除冷数据,下面以redis为例:
上一篇:大数据杀熟如何规范大数据杀熟还
下一篇:如何注册qq号 怎样注册qq号
Copyright © 2024 有趣生活 All Rights Reserve吉ICP备19000289号-5 TXT地图HTML地图XML地图