admin 管理员组文章数量: 887021
文章目录
- 前端缓存|后端缓存
- 按缓存位置分类
- 缓存过程
- memory cache
- disk cache
- Service Worker
- Push Cache
- 强制缓存
- 适用场景
- Expires
- Cache-Control
- 协商缓存(也叫对比缓存)
- 适用场景
- ETag和Last-Modified区别
- 缓存相关问题
- 缓存穿透
- 缓存击穿
- 缓存雪崩
前端缓存|后端缓存
- 后端缓存主要集中于“处理”步骤,通过保留数据库连接,存储处理结果等方式缩短处理时间,尽快进入“响应”步骤。
- 前端缓存则可以在两步:“请求”和“响应”中进行。在“请求”步骤中,浏览器也可以通过存储结果的方式直接使用资源,直接省去了发送请求;而“响应”步骤需要浏览器和服务器共同配合,通过减少响应内容来缩短传输时间。这些都会在下面进行讨论。
按缓存位置分类
它们的优先级是:(由上到下寻找,找到即返回;找不到则继续)
- Service Worker
- Memory Cache
- Disk Cache
- Push Cache
- 网络请求获取资源
缓存过程
- 浏览器第一次发起HTTP请求, 在浏览器缓存中没有发现请求的缓存结果和缓存标识
- 因此向服务器发起HTTP请求, 获得该请求的结果还有缓存规则(也就是Last-Modified 或者ETag)
- 浏览器把响应内容存入Disk Cache, 把响应内容的引用存入Memory
- Cache把响应内容存入 Service Worker 的 Cache Storage (如果 Service Worker 的脚本调用了 cache.put())
下一次请求相同资源的时候:
- 调用Service Worker 的fetch事件响应
- 查看memory Cache
- 查看disk Cache.
memory cache
memory cache 是内存中的缓存,(与之相对 disk cache 就是硬盘上的缓存)。按照操作系统的常理:先读内存,再读硬盘。
-
浏览器占用的内存不能无限扩大这样两个因素,memory cache 注定只能是个“短期存储”。常规情况下,浏览器的 TAB 关闭后该次浏览的 memory cache 便告失效 (为了给其他 TAB 腾出位置)。而如果极端情况下 (例如一个页面的缓存就占用了超级多的内存),那可能在 TAB 没关闭之前,排在前面的缓存就已经失效了。
- 一旦关闭Tab标签页,内存中的缓存也就释放了,所以容量和存储时效上差些
- 当渲染进程结束后,内存缓存也就不存在了。
-
是浏览器为了加快读取缓存速度而进行的自身的优化行为,不受开发者控制,也不受 HTTP 协议头的约束,算是一个黑盒。
-
几乎所有的请求资源都能进入memory Cache, 细分来说主要分为preloader和preload这两块.
- preloader:作用主要是用于在浏览器打开一个网页的时候,能够一边解析执行js/css, 一边去请求下一个资源, 而这些被 preloader 请求来的资源就会被放入 memory Cache 中,供之后的解析执行操作使用
- preload:能显式指定预加载的资源, 这些资源也会被放进memory Cache中, 例如
-
在从memory Cache读取缓存时, 浏览器会忽视Cache-Control中的一些max-age、no-cache等头部配置, 除非设置了no-store这个头部配置.
disk cache
也叫 HTTP cache,顾名思义是存储在硬盘上的缓存,因此它是持久存储的,是实际存在于文件系统中的。而且它允许相同的资源在跨会话,甚至跨站点的情况下使用,例如两个站点都使用了同一张图片。
存储容量更大, 且存储时长更长.
- disk cache 会严格根据 HTTP 头信息中的各类字段来判定哪些资源可以缓存,哪些资源不可以缓存;哪些资源是仍然可用的,哪些资源是过时需要重新请求的。当命中缓存之后,浏览器会从硬盘中读取资源,虽然比起从内存中读取慢了一些,但比起网络请求还是快了不少的。绝大部分的缓存都来自 disk cache。
- 强制缓存,协商缓存都来自于此
- 即使是跨域站点的情况下,相同地址的资源一旦被硬盘缓存下来,就不会再次请求。
Memory Cache与Disk Cache两者的对比:
- 比较大的JS、CSS文件会被丢硬盘中存储, 反之则存储在内存中
- 当前系统内存使用率比较高的时候,文件优先进入磁盘
Service Worker
是运行在浏览器背后的独立线程, 也就是说它脱离了浏览器的窗体, 无法直接访问DOM.功能上主要是能实现: 离线缓存、消息推送、网络代理等.比如离线缓存就是Service Worker Cache.
但 Service Worker 的出现,给予了我们另外一种更加灵活,更加直接的操作方式。我们现在可以绕开银行职员,自己把钱放进去或者取出来。因此我们可以选择放哪些钱(缓存哪些文件),什么情况把钱取出来(路由匹配规则),取哪些钱出来(缓存匹配并返回)。当然现实中银行没有给我们开放这样的服务。
- 使用Service Worker会涉及到请求拦截, 所以需要用HTTPS协议来保证安全, 传输协议必须是HTTPS
- Service Worker同时也是PWA的重要实现机制
- Service Worker 是由开发者编写的额外的脚本,与浏览器其它内建的缓存机制不同, 它可以让我们自由的控制缓存哪些文件、如何匹配读取缓存, 且缓存是持续性的
- 我们可以从 Chrome 的 F12 中,Application -> Cache Storage 找到这个单独的“小金库”。
- 这个缓存是永久性的,即关闭 TAB 或者浏览器,下次打开依然还在(而 memory cache 不是)。有两种情况会导致这个缓存中的资源被清除:手动调用 API cache.delete(resource) 或者容量超过限制,被浏览器全部清空。
- 如果 Service Worker 没能命中缓存,一般情况会使用 fetch() 方法继续获取资源。这时候,浏览器就去 memory cache 或者 disk cache 进行下一次找缓存的工作了。注意:经过 Service Worker 的 fetch() 方法获取的资源,即便它并没有命中 Service Worker 缓存,命中其他缓存甚至实际走了网络请求,也会标注为 from ServiceWorker
Push Cache
推送缓存, 它是浏览器缓存的最后一段防线, 当以上三种缓存都没有命中的时候, 它才会被使用,是HTTP/2中的内容。它只会在会话(Session)中存在, 一旦会话结束它就会被释放, 并且缓存时间也很短暂, 在Chrome浏览器中只有5分钟.
另外由于它是 HTTP/2 中的内容, 因此在国内不是很普及
- 所有的资源都能被推送,并且能够被缓存,但是 Edge 和 Safari 浏览器支持相对比较差
- 可以推送 no-cache 和 no-store 的资源
- 一旦连接被关闭,Push Cache 就被释放
- 多个页面可以使用同一个HTTP/2的连接,也就可以使用同一个Push Cache。这主要还是依赖浏览器的实现而定,出于对性能的考虑,有的浏览器会对相同域名但不同的tab标签使用同一个HTTP连接。
- Push Cache 中的缓存只能被使用一次
- 浏览器可以拒绝接受已经存在的资源推送
- 可以给其他域名推送资源
强制缓存
可以造成强制缓存的字段是 Cache-control(http1.1) 和 Expires(http1.0),强缓存是不需要发送HTTP请求的,返回的状态码都是200, 而协商缓存需要
两者同时存在, Cache-control的优先级更高;
适用场景
对于不常变化的资源使用
- 比如图片、字体等
Expires
- 这是 HTTP 1.0 的字段,表示缓存到期时间,是一个绝对的时间 (当前时间+缓存时间),如Expires: Thu, 10 Nov 2017 08:45:11 GMT
- 在响应消息头中,设置这个字段之后,就可以告诉浏览器,在未过期之前不需要再次请求
- 缺点:由于是绝对时间,用户可能会将客户端本地的时间进行修改,而导致浏览器判断缓存失效,重新请求该资源。此外,即使不考虑客户端修改,时差或者误差等因素也可能造成客户端与服务端的时间不一致,致使缓存失效
Cache-Control
- cache-control优先级高于Expires
- 已知Expires的缺点之后,在HTTP/1.1中,增加了一个字段Cache-control,该字段表示资源缓存的最大有效时间,在该时间内,客户端不需要向服务器发送请求
Cache-Control请求头常见属性
Cache-Control响应头常见属性
- 其中no-cache:表示下次请求不要直接使用缓存而需要比对,并不对本次请求进行限制,不进行强缓存验证, 而是用协商缓存来验证,使得浏览器每次都请求服务器, 然后配合ETag或者Last-Modified来验证资源是否有效.
- public: 客户端和代理服务器都可以缓存. 响应可以被中间任何的一个节点缓存, 比如一个请求要经历 Browser -> proxy1 -> proxy2 -> Server, 中间的代理(proxy)可以缓存资源. 下次再请求同一资源的时候, 浏览器就会直接到proxy1中拿缓存的东西而不必向proxy2拿.
协商缓存(也叫对比缓存)
当强制缓存失效(超过规定时间)时,就需要使用对比缓存,由服务器决定缓存内容是否失效
- 对比缓存在请求数上和没有缓存是一致的,但如果是 304 的话,返回的仅仅是一个状态码而已,并没有实际的文件内容,因此 在响应体体积上的节省是它的优化点。
- 返回304和Not Modified(空的响应体)
适用场景
对于常变化的资源使用,设置Cache-Control: no-cache
- 比如 HTML 文件、JavaScript 文件
对比缓存是可以和强制缓存一起使用的,作为在强制缓存失效后的一种后备方案。当强缓存失效后采用协商缓存来判断是否使用缓存
对比缓存有 2 组字段:
Last-Modified & If-Modified-Since
- 浏览器第一次向服务器请求这个资源
- 服务器在返回这个资源的时候, 在response header中添加Last-Modified告知客户端,资源最后一次被修改的时间,例如 Last-Modified: Mon, 10 Nov 2018 09:10:11 GMT
- 浏览器将这个值和内容一起记录在缓存数据库中。
- 下一次请求相同资源时时,浏览器从自己的缓存中找出“不确定是否过期的”缓存。因此在请求头中将上次的 Last-Modified 的值写入到请求头的 If-Modified-Since 字段
- 服务器会将 If-Modified-Since 的值与 Last-Modified 字段进行对比。如果相等,则表示未修改,响应 304和一个空的响应体, 告诉浏览器从自己(浏览器)的缓存中拿;反之,则表示修改了,响应 200 状态码,并返回数据
缺点:
- 如果资源更新的速度是秒以下单位,那么该缓存是不能被使用的,因为它的时间单位最低是秒。
- 如果文件是通过服务器动态生成的,那么该方法的更新时间永远是生成的时间,尽管文件可能没有变化,所以起不到缓存的作用。
- 如果本地打开了缓存文件,即使没有对文件进行修改,但还是会造成Last-Modified被修改,服务器端不能命中缓存导致发送相同资源
Etag & If-None-Match
服务器会根据当前文件的内容, 给文件生成一个唯一的标识, 若是文件发生了改变, 则这个标识就会改变.
- 对于 Etag 分为强 ETag和弱 Etag
- 强 ETag:强 ETag 表示资源的字节级别的完全相等。当两个资源的强 ETag 值相同时,意味着这两个资源在内容和属性(如最后修改时间)上完全相同。强 ETag 值直接使用双引号括起来的字符串(如 “12345”)
- 弱 ETag:弱 ETag 表示资源在语义上是相等的,但在字节级别上可能存在差异。当两个资源的弱 ETag 值相同时,意味着这两个资源在功能和表现上是相同的,但在内容和属性(如最后修改时间)上可能有细微差别。弱 ETag 值以 W/ 开头,后面跟一个双引号括起来的字符串(如 W/“12345”)
- 弱 ETag 通常用于动态生成的资源(如 HTML 页面)的缓存控制,以提高性能,同时允许一定程度的差异
etag: W/"132489-1627839023000"
if-none-match: W/"132489-1627839023000"
- Etag 存储的是文件的特殊标识(一般都是 hash 生成的),服务器存储着文件的 Etag 字段。之后的流程和 Last-Modified 一致,只是 Last-Modified 字段和它所表示的更新时间改变成了 Etag 字段和它所表示的文件 hash,把 If-Modified-Since 变成了 If-None-Match。服务器同样进行比较,命中返回 304, 不命中返回新资源和 200。
- 服务器会将这个标识ETag放到响应体的header中与请求的资源一起返回给浏览器, 而浏览器同样也会缓存文件与这个header
- 在下一次再次加载该资源时, 浏览器会将刚刚缓存的ETag放到请求体头部(request header)的If-None-Match里发送给服务器.
- Etag 的优先级高于 Last-Modified
作用场景
- 协商缓存一般存储:HTML
ETag和Last-Modified区别
- 若是本地打开了缓存文件, 并没有进行修改, 也还是会改变最后修改时间, 导致缓存失败;
- 由于Last-Modified是以秒来计时的, 若是某个文件在一秒内被修改了很多次, 那么这时候的 Last-Modified 并没有体现出修改了
- 准确度上ETag更强;
- 性能上Last-Modified更好;
- ETag优先级更高
浏览器的行为
- 打开网页,地址栏输入地址: 查找 disk cache 中是否有匹配。如有则使用;如没有则发送网络请求。
- 普通刷新 (F5):因为 TAB 并没有关闭,因此 memory cache 是可用的,会被优先使用(如果匹配的话)。其次才是 disk cache。
- 强制刷新 (Ctrl + F5):浏览器不使用缓存,因此发送的请求头部均带有 Cache-control: no-cache(为了兼容,还带了 Pragma: no-cache)。服务器直接返回 200 和最新内容。
实际页面效果
-
首次请求.js、.css 和 .jpg
-
再次请求 (F5)
-
关闭tab后打开
缓存相关问题
缓存穿透
- 缓存穿透是指查询一个不存在的数据(既不在缓存中,也不在数据库中),由于缓存中没有该数据,导致请求穿透到数据库,从而对数据库造成压力
- 布隆过滤器:对于可能访问的数据,可以将它们的 key 存储在布隆过滤器中。当用户请求数据时,首先检查请求的 key 是否在布隆过滤器中。如果不在,说明数据肯定不存在,可以直接返回错误信息。如果在,再继续访问缓存层和数据库。
- 缓存空对象:在缓存中设置一个空值,并设置一个较短的过期时间,下次请求时直接从缓存中返回空数据,避免请求穿透到数据库。
缓存击穿
- 缓存击穿是指一个热点数据在缓存中过期或者被清除,导致大量请求同时访问数据库,从而对数据库造成压力。
- 热点数据永不过期:将热点数据设置为永久不过期,避免缓存失效导致请求穿透到数据库。
- 加锁:在缓存失效的时候,使用分布式锁或者互斥锁,避免大量请求同时访问数据库。
- 限流:使用限流算法对请求进行限制,避免大量请求同时访问数据库。
缓存雪崩
- 缓存雪崩是指缓存中大量的数据在同一时间失效,导致大量请求同时访问数据库,从而对数据库造成压力。
- 数据过期时间随机:将缓存数据的过期时间设置为随机时间,避免大量缓存同时失效。
- 数据预热:在系统启动时,将热点数据加载到缓存中,避免缓存失效时大量请求访问数据库。
- 分布式部署:将缓存部署在多个节点上,避免某个节点失效导致缓存雪崩。
版权声明:本文标题:浏览器缓存 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.freenas.com.cn/jishu/1723990590h746199.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论