缓存是什么?为什么要做缓存?常见的缓存机制有哪些?缓存是如何工作的?

定义

这里讨论的缓存为浏览器缓存。通过复用以前获取的资源来提高网站性能。

缓存优点

  • 减少对服务器的访问次数,减轻服务器压力;
  • 节省带宽;
  • 再次访问时提高了访问速度,提升了用户体验。

强缓存(本地缓存)

强制使用缓存方案,直接使用本地缓存,不与服务器通讯。成功状态码为 200。

控制

由 header 中的两个字段控制,分别为 expires 和 cache-control。

  • expires(截止日期,http1.0规范):客户端时间在截止日期之前的时间,都直接使用本地缓存(本地已存在缓存,客户端时间与服务器时间有偏差时,可能导致缓存失效);
  • cache-control(缓存控制,优先级高于expires,http1.1规范)
    • max-age=value:value 表示资源的最大有效时间的“秒数”,如果最新一次请求时间还小于资源第一次请求时间加上这个 value,则使用本地缓存,不会因为客户端时间与服务器时间有偏差而导致缓存失效;
    • public:表示客户端和代理服务器(如CDN)都可以缓存;
    • private(默认值):表示只有客户端可以缓存;
    • no-cache:客户端缓存内容,但是是否使用缓存需要经过协商缓存来验证决定;
    • no-store:所有内容都不会被缓存,即不使用强缓存,也不使用协商缓存;
    • must-revalidate:如果缓存的内容失效,请求必须发送到服务器/代理以进行重新验证。

弱缓存(协商缓存)

强制缓存失效后,浏览器携带缓存标识向服务器发起请求,由服务器根据缓存标识决定是否使用缓存的过程。成功状态码为 304。

控制

由请求头中的 If-Modified-Since 和 If-None-Match 与响应头对应的 Last-Modified 和 ETag 来控制(必须成对使用才有效果)。

Last-Modify/If-Modified-Since

过程

  • 第一次请求时,在 response header 中返回 Last-Modify,表示最后一次修改时间;
  • 下次请求是,request header 中包含 If-Modify-Since,去询问服务器是不是还是上次那个最后修改时间;
  • 如果还是上次的一样的时间,那么说明数据没有更新,服务端返回 304,浏览器直接从缓存中获取就行了;
  • 如果不是上次不是上次的时间了,那么就返回数据,同时返回 Last-Modify。

缺点

  • Last-Modified 保存的是绝对时间,并且是精确到秒,所以如果资源在1秒内修改了多次的话,那就无法识别;
  • 对于文件只改变了修改时间,内容不变,这时候也会使缓存失效,其实这个时候我们是不希望客户端重新请求的;
  • 某些服务器不能精确的得到文件的最后修改时间。

ETag/If-None-Match

这两个值是由服务器生成的资源唯一表示字符串,只要资源有变化这个值就会变化。判断过程同上,不同的是当服务器返回 304 时,由于 ETag 重新生成过,response header 中还是会将 ETag 返回,即使和原来的是一样的。

优点
除缓存的优点外,还解决了 Last-Modify/If-Modified-Since 的缺点。

缺点
ETag虽然能解决问题,但也并非完美,ETag 每次服务端生成都需要进行读写操作(因为要生成 hash),而 Last-Modified 只需要读取操作,ETag 消耗更大一些。

同时使用

Last-Modified 与 ETag 一起使用时,服务器会优先验证 ETag,一致的情况下,才会继续比对 Last-Modified,最后才决定是否返回 304。