缓存技术在Web应用中也是一项关键技术,在提升性能方面起了重要的作用。
缓存命中率
本质上,缓存是否有效依赖于你能多少次重用一个缓存响应业务请求,这个度量指标就是缓存命中率。她是应用缓存技术时简单而重要的一项指标。
影响缓存命中率的因素:缓存键集合大小、内存空间和缓存寿命。
- 缓存中每个对象使用缓存键进行识别,定义一个对象的唯一方式就是对象缓存键执行精确匹配。键数量越少,缓存的效率越高。
- 内存空间的大小直接决定了缓存对象的平均大小和缓存对象数量。物理上能缓存的对象越多,缓存命中率就越高。
- 对象缓存的时间越长,缓存对象被重用的可能性就越高。
基于HTTP的缓存
通读缓存:一个缓存组件,它能给客户端返回缓存资源,或在请求未命中缓存时获取实际数据。
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,这样数据就不会太陈旧。