关于缓存

笔记作者:ggddll123

笔记链接:http://www.w3cfuns.com/blog-5396734-5399381.html

原创声明:此笔记被 ggddll123 标注为原创笔记,未经作者同意转载必须保留此段声明,且在文章页面明显位置给出原文链接,否则保留追究法律责任的权利。

关于缓存的二三事

本文主要想讲一下缓存的一些详细过程,为了让大家容易看懂,并且可以做出选择性的了解整个缓存的机制,我在这里从问题入手,逐步剖析缓存中的原理和用户代理(可以简单认为是浏览器)在这里所做的操作。

问题:

  • 缓存和304的区别与联系
  • http响应首部中Age和Max-Age的区别与联系
  • 缓存过期一定会再验证吗
  • Cache-Control:no-cache;和Cache-Control:max-age:0;的区别

如果以上问题,你完全没有疑问,那你完全可以略过此文了~

缓存和304的区别

欲了解这个问题,我们首先需要弄清楚浏览器处理缓存的整个流程。请看下图:

如图所示,当有请求到达的时候,用户代理首先会检查有没有缓存,以及缓存是否新鲜,如果有缓存并且新鲜就直接提供给客户端,如果不是,那么这时候就会触发一个与服务器再验证的过程。如果在验证过程发现文件没有变化,那么就会返回一个304。到此,大家应该明白缓存和304的区别,实际上是两个独立的过程,首先是缓存的检查,然后是服务器端的再验证,304是服务器端验证的返回值之一,如果服务器端验证发现文件更新了,则会直接以200返回。

既然说到了服务器再验证,那么下面我们详细说一下服务器再验证的过程。

HTTP定义了5个条件再验证首部,对缓存验证来说,最重要的首部是

if-modified-since和if-none-match

其他条件首部是:if-unmodified-since,if-range,if-match。

if-modified-since和last-modified配合使用。服务器传回的时间在last-modified中,再验证时,就用if-modified-since把这个时间带回给服务器。

if-none-match 和 e-tag配合使用。服务器传回的tag在e-tag中,再验证时,就用if-none-match把这个tag带回给服务器验证。

验证流程图如下:

大部分情况下,我们都使用if-modified-since,也就是使用修改时间来验证就足够了,但是有些情况例外,所以HTTP协议为我们准比了e-tag,简单来说,如果文件需要周期性的使用相同的文件,那么修改时间就无法满足需求了。

具体无法使用last-modified再验证的情况如下,

  • 有些文档可能周期性的重复。内容没有变化,但修改日期发生变化。
  • 有些文档被改了,但是修改不重要,不需要重新加载。
  • 有些服务器无法准确判断其页面的最后修改时间。
  • 有些服务器提供的文档会在亚秒之间发生变化,这样last-modified提供以秒为单位的验证就不准确了。

http响应首部中Age和Max-Age的区别与联系

说起这个,要从Age首部的由来说起,然后又不得不说起用户代理计算缓存使用期的方法。

首先,缓存是否过期取决于两个值:生命期 和 新鲜度值

Age首部来自于生命期的计算,我们首先来看看生命期的计算方法:

HTTP使用期计算的细节有点棘手,但其基本概念很简单。响应到达缓存时,缓存可以用过查看Date首部或者Age首部来判断响应已使用的时间。缓存还能记录下文档在本地缓存中停留的时间。把这些值加起来就是缓存的总使用期。

1. 表面使用期

$apparent_age = max(0,$time_got_response - $Date_header_value);

2. 逐跳使用期

由于本地的时间不一定准确,所以表面使用期肯定会存在误差。HTTP/1.1 会让每台设备都将相对使用期累加到Age首部中去,以此来解决缺乏通用同步时钟的问题。这种方式并不需要进行跨服务器的、端到端的时钟对比。

除了基于Date计算出来的Age之外,还使用了相对Age值,而且不论是跨服务器的Date值,还是计算出来的Age值都可能被低估,所以会选择使用估计出的两个Age值中最保守的那个。

$apparent_age = max(0,$time_got_response - $Date_header_value);$corrected_apprent_age = max($apparent_age, $Age_header_value);$response_delay_estimate = $time_got_response - $time_issued_request;$age_when_document_arrived_at_our_cache = $corrected_apparent_age + $response_delay_estimate;

3. 对网络延时的补偿

浏览器会把浏览器请求缓存的时间和收到缓存的时间计算一个差值来补偿网络延时。

$apparent_age = max(0,$time_got_response - $Date_header_value);$corrected_apprent_age = max($apparent_age, $Age_header_value);$age_when_document_arrived_at_our_cache = $corrected_apparent_age;

4. 完整使用期计算

关注第2点,逐跳使用期的计算中,Age是用来把缓存在每台设备中停留的时间累加用的,这个值只会出现在有多级缓存的请求中,不得不提一下,缓存也是有拓扑结构的,可能会有层级缓存,兄弟缓存等,但这不是本文的重点,如有需要,自行了解。

而max-age,这个值的用法是 cache-control:max-age:600

以秒为单位,用来控制缓存的新鲜度用的,当设置cache-control:max-age:600,表示该缓存可以有600s的新鲜期。一般情况下,如果在这个时间内,就直接使用缓存,超出这个时间就需要向服务器发起在验证。

顺便提一下expires,不推荐使用这个值,因为expires的值是一个具体的时间点,因为客户端时间可能和服务器不一致,这极可能导致缓存不准。

缓存过期一定会再验证吗

欲了解这个问题,我们就需要了解cache-control的几个值了。

  • cache-control:max-stale

缓存可以随意使用过期文件。

  • cache-control:max-stale=<s>

最多在未来s秒内,可以使用过期的缓存,这就放宽的缓存限制。

  • cache-control:min-refresh=<s>

至少在未来s秒内文档要保持新鲜,这就收紧了缓存限制。

  • cache-control:no-store

no-store 会直接删除改缓存对象。

  • cache-control:no-cache

no-cache 表示缓存是可以用的,但是在使用之前必须再验证。

  • cache-control:must-revalidate

must-revalidate 表示每次使用缓存都必须再验证。

  • cache-control:max-age

以秒为单位,设置缓存的新鲜度,推荐使用。

  • expires

一个绝对时间,设置缓存将在那个时刻过期。 不推荐使用expires,因为很多服务器时间都不同步。

看到这里应该明白了,缓存即使过期也可以使用max-stale来强制使用过期的缓存,当然,就算缓存没过期,也同样可以使用min-refresh来强制不让使用。

Cache-Control:no-cache;和Cache-Control:max-age:0;的区别

有了上一题的铺垫,这个问题很好回答了,Cache-Control:max-age:0;仅仅说明了当前缓存过期,默认情况下,浏览器会再验证。但这不是绝对的。某些情况下,有可能需要用到过期的缓存而不用再验证,那么你可以这么写 Cache-Control:max-age:0,max-stale=50;,这个时候就会使用过期的缓存了。而Cache-Control:no-cache;说明除非资源进行了在验证,否则不允许使用缓存。所以如果严格来说的话,Cache-Control:max-age:0,must-revalidate;的意思才和Cache-Control:no-cache;是一样的。

好了,关于缓存的一些东西,这里就介绍完了,当然并不是全部,关于缓存的拓扑结构缓存新鲜度值的具体计算缓存的弱验证,以及缓存试探性过期,如果有兴趣,读者依然可以继续阅读其他的文献。

时间: 2024-10-19 20:49:06

关于缓存的相关文章

Apache Ignite——新一代数据库缓存系统

Apache Ignite是一个通用的数据库缓存系统,它不仅支持所有的底层数据库系统,比如RDBMS.NoSQL和HDFS,还支持Write-Through和Read-Through.Write-Behind Caching等可选功能. Apache Ignite是一个聚焦分布式内存计算的开源项目,它在内存中储存数据,并分布在多个节点上以提供快速数据访问.此外,可选地将数据同步到缓存层同样是一大优势.最后,可以支持任何底层数据库存储同样让 Ignite成为数据库缓存的首先.

数据字典实现缓存

数据字典的好处很多比如: 1.可以减少使用表,来专门记录类型. 2.类型使用key检索,或者报表统计分析,在一定程度上相比汉字来讲,效率好得多. 3.使用缓存的数据字典.也可以减少不少的io操作. 等等.... 首先,库表设计就智者见智了.不多说.爱怎么设计就怎么设计. 完整的数据字典设计 ,需要 1.生成select 自定义标签. 2.list页面,或者get页面, 一个key转 value的标签 使用自定义标签,搭配上缓存的数据字典是最方便.最完美的解决办法, 接下来,就直接贴代码了. 一.

4.缓存控制技术

动态网站的数据都是从数据库获取而来的.所以网站的瓶颈往往就是反复连接数据库和大量的SQL语句查询的执行.由于HTTP协议是无状态性的,所以每次对页面请求都会执行相同的操作.我们可以让页面内容本身变化不大但是偶尔还是要变化的页面(例如新闻网站)缓存起来作为静态的页面,下一次再访问的时候直接访问静态的HTML页面即可. ① Smarty里面控制缓存 需要做3步工作:开启缓存,指定缓存目录,定义缓存的生命周期

Shiro缓存(十三)

使用缓存,可以解决每次访问请求都查数据库的问题.第一次授权后存入缓存. 缓存流程 shiro中提供了对认证信息和授权信息的缓存.shiro默认是关闭认证信息缓存的,对于授权信息的缓存shiro默认开启的.主要研究授权信息缓存,因为授权的数据量大. 用户认证通过. 该 用户第一次授权:调用realm查询数据库 该 用户第二次授权:不调用realm查询数据库,直接从缓存中取出授权信息(权限标识符). -------------------------------------使用ehcache缓存框架

Hibernate session缓存

一级缓存(执行代码时查看console台上的sql语句)  清空缓存 @Test public void demo03(){ //清空缓存 Session session=factory.openSession(); session.beginTransaction(); //1.查询 User user = (User)session.get(User.class, 1); System.out.println(user); //session.evitc(user) //将执行对象从一级缓存

nginx三 之缓存模块

友情提示: 缓存模块是在动静分离的环境基础上搭建,动静分离可以参考http://www.cnblogs.com/dahuandan/p/6759212.html 介绍 提高网站响应速度是web应用不容忽视的目标,在之前动静分离的基础上,我们已经降低了后端服务器压力,提高了处理请求的性能,但是用户请求的静态资源是从硬盘读取,相比内存的性能还有很大的提高: Nginx自带的缓存模块可以把静态资源缓存到内存中,提高了用户请求静态资源的速度,并且nginx自带缓存模块配置简单,使用灵活,搭配第三方插件可

一个缓存容灾写的样例

背景 有时我们能够使用缓存进行容灾的处理.场景例如以下:我们当前有一个专门提供各种数据的应用DataCore,该应用开放多个RFC方法供其它应用使用.      我们平时在读写数据时,会在Cache备份一份(为平时DataCore提高响应速度.减少DB.CPU压力所用),当DB挂掉的时候.Cache还能够用来容灾.使用缓存容灾的优点是:性能足够好,坏处是缓存可比数据库成本高多了. 让我们想象得更猛烈些,当DataCore整个挂掉的时候,A.B.C.D方怎么才干安然的执行下去? 我们能够在A.B.

浏览器缓存机制浅析

非HTTP协议定义的缓存机制 浏览器缓存机制,其实主要就是HTTP协议定义的缓存机制(如: Expires: Cache-control等).但是也有非HTTP协议定义的缓存机制,如使用HTML Meta 标签,Web开发者可以在HTML页面的<head>节点中加入<meta>标签,代码如下: <META HTTP-EQUIV="Pragma" CONTENT="no-cache"> 上述代码的作用是告诉浏览器当前页面不被缓存,每

缓存与数据库的一致性思考

时隔两年,重新启动这个博客.熟悉又有点陌生,这两年我的技术方向有了很大改变,但由于一直在使用为知笔记,因此这些改变没有提现在本博客上.之所以重启这个博客,主要是因为博客是一个开放的东西,可以带来一些交流,而笔记则是个人的东西,缺少思维碰撞.闲话少叙,这就开始. 问题:怎么保持缓存与数据库一致? 要解答这个问题,我们首先来看不一致的几种情况.我将不一致分为三种情况: 1. 数据库有数据,缓存没有数据: 2. 数据库有数据,缓存也有数据,数据不相等: 3. 数据库没有数据,缓存有数据. 在讨论这三种

构建高效可申缩的结果缓存

摘自<<JAVA并发编程实战>> public interface Computable<A, V> { V comput(A arg); } import java.util.concurrent.*; /** * 构建高效可申缩的结果缓存 * <p> * author: shiruiqiang * time: 31/01/2017 23:11 **/ public class Memoizer<A, V> implements Computa