Web应用中的缓存

缓存技术在Web应用中也是一项关键技术,在提升性能方面起了重要的作用。

缓存命中率

本质上,缓存是否有效依赖于你能多少次重用一个缓存响应业务请求,这个度量指标就是缓存命中率。她是应用缓存技术时简单而重要的一项指标。

影响缓存命中率的因素:缓存键集合大小、内存空间和缓存寿命。


  1. 缓存中每个对象使用缓存键进行识别,定义一个对象的唯一方式就是对象缓存键执行精确匹配。键数量越少,缓存的效率越高。

  2. 内存空间的大小直接决定了缓存对象的平均大小和缓存对象数量。物理上能缓存的对象越多,缓存命中率就越高。

  3. 对象缓存的时间越长,缓存对象被重用的可能性就越高。

基于HTTP的缓存

通读缓存:一个缓存组件,它能给客户端返回缓存资源,或在请求未命中缓存时获取实际数据。

客户端与通读缓存交互.png

HTTP缓存头

大部分HTTP头信息是可选的,被用以协调期望的行为。

Cache-Control

Cache-Control允许你指定多个选项。
具体的设置可以参考:Cache-Control

Expries

它指定一个缓存对象失效的绝对时间点。

Web响应的过期时间可以由Cache-Control:max-age=600定义,也可以使用Expries头定义一个绝对的时间值。
在响应中同时包含这两种头是冗余的,会导致混乱和潜在的不一致性。因此,推荐使用你想用的头并固定使用它们,而不是将所有可能的头都用在响应中。

Vary

这个头的目的是高速缓存你需要基于某些HTTP请求头,生成响应的多个变体。

其他的HTTP缓存可以参考:HTTP缓存

HTTP缓存技术类型

目前主要有4种HTTP缓存:浏览器缓存、缓存代理、反向代理和CDN。

伸缩HTTP缓存

我们在进行伸缩HTTP缓存的时候,不用去担心浏览器缓存、第三方缓存代理和CDN的伸缩性。应该重点关注反向代理。

要想有效地伸缩反向代理层,一定要被关注的是缓存命中率:
1)缓存键空间。描述了反向代理在一段时间内需要记录多少唯一URL的数量。
2)平均响应存活时间TTL。它描述了响应被缓存的时间。
3)缓存对象的平均大小。它影响了反向代理能够使用多大的内存或存储,来保存最常访问的对象。

缓存应用对象

定制对象缓存。对象缓存的使用方式和HTTP缓存不同,它是旁路缓存而不是通读缓存。在旁路缓存中,应用需要意识到缓存对象的存在,旁路缓存主动存储和获取对象,缓存并不是透明地位于应用和数据之间。

旁路缓存被应用视为一个独立的键值对存储。应用代码通常会询问对象缓存需要的对象是否存在,如果存在,它会获取并使用缓存的对象。如果需要的对象不存在或已过期,应用会做必要的工作从头构建对象,并将其保存会对象缓存中以便将来使用。

对象缓存的一般类型

客户端缓存
可以通过JavaScript把对象缓存到客户端浏览器中(localStorage等,key-value的形式)。

本地缓存
将对象直接缓存到服务器上。通常有以下几种实现方式:

  • 对象直接缓存在应用内存
  • 对象存储在共享内存,同一台机器的多个进程可以访问它们
  • 缓存服务器作为独立应用部署在Web服务器上

本地缓存的好处:持久化和访问速度都很快;开发和部署简单;
本地缓存的缺点:服务器间很多重复的数据;缓存无法保持一致;

实现的框架有:EhCache等

分布式对象缓存
分布式缓存与本地缓存主要的差别是:与分布式缓存交互需要与缓存服务器进行网络通信。分布式缓存提供了比本地缓存更好的伸缩性。

实现的技术有:Redis、Memcached等

伸缩对象缓存

当要进行对象缓存伸缩时:

  • 选择的方案取决于缓存的位置和类型。也就是根据业务选择客户端缓存、本地缓存或者分布式缓存
  • 使用数据复制,或者混合使用数据分区和数据复制。

缓存的经验法则

缓存整个调用栈

关于缓存,其中最重要的一点就是:你能缓存的调用栈越高,能节省的资源就越多。

相比于仅仅缓存用来构建页面的数据库查询结果,如果你缓存整个页面片段,显然可以节省更多时间及资源。缓存的终极目的是避免Web请求到达Web服务器,即便做不到,你仍需要尽量地缓存整个调用栈。

用户间缓存重用

尽可能多地在不同请求或用户间重用相同的缓存对象。缓存对象如果不被再次请求,那么就是时间和资源的浪费。

关键点在于你需要最大化缓存命中率,要做到这一点,你可以通过增加缓存池,延长缓存对象TTL,减少可能的缓存键的数量来实现。

从哪儿开始使用缓存?

如果你在优化一个没有充分使用缓存的Web应用,你需要问自己,从哪儿入手?哪些查询是重要且需要缓存的?哪些页面值得缓存?哪些服务需要的缓存最多?因为就优化而言,都需要基于一个严格而简单的度量标准进行优先级排列,而不是仅仅依靠直觉。要评估哪些地方需要缓存,可以用生成特定响应的聚合时间来度量。聚合时间可以采用以下方式计算:聚合时间=每个请求消耗的时间请求数量*。

缓存失效的困难

缓存失效会很快使应用的处理变得困难起来。

缓存失效最好的替代方案就是在缓存对象上设置一个较短的TTL,这样数据就不会太陈旧。