内容纲要

随着Web业务的复杂和并发的增加,应用服务器和数据库服务器所做的计算也越来 越多,但是我们的应用服务器资源是有限的,数据库每秒接受并处理请求的次数也是有限 的。如何利用有限的资源提供尽可能大的吞吐量呢?

一个办法:减少计算量,缩短请求流程(减少网络 I/O 或者硬盘 I/O),这时缓存就可 以大展身手了。缓存的基本原理就是打破标准流程,在标准流程中任何环节都可以被切 断。请求可以从缓存里取到数据直接返回。这样能够节省时间、提高响应速度、节省硬件资源,可以让有限的硬件资源服务更多的用户。

在Web世界,理论上每一层都可以被缓存。以PHP应用为例:

  • 底层有CPU缓存、磁盘文件系统缓存。
  • 应用层有Zend虚拟机的变量缓存,有Memcached这样的Key Value内存缓存, 有APC、eAccelerator这类基于Opcode字节码的缓存。
  • 数据库层有Table Cache、 Thread Cache、 Query Cache。
  • Servlet容器层有Apache的缓存。
  • Servlet再上去一点,有一个Web Cache层(如Squid、Varnish等),然后应用程 序代码级别的Smarty实现的文件缓存。基于HTTP协议和浏览器自身实现的浏览器缓存。 当然,上面的分类只是大致描述,其中存在重复和交叉。

此处,NoSQL系列缓存(NoSQL不是Not SQL,而是Not only SQL ),包括 Memcacheds Cassandra、MongoDB、Redis、 Tokyo Tyrant等产品。NoSQL同样是一种存储数 据的数据库,主要基于内存和Key Value模式(此外,还有基于document、 XML 的)。由于NoSQL通常基于内存,所以同时还具有内存缓存的作用。故常有人困惑或争执 其是缓存软件还是数据库软件。其实这可以从其命名看出,如Memcached偏向于缓存, 追求速度和性能;MongoDB偏向于数据库,数据类型较丰富。因此,NoSQL作为一种 统称,同时具有Key Value数据库和内存缓存的功能。

缓存放在什么地方呢?无非就是内存和硬盘。文件缓存(如模板机制)自然缓存在硬 盘上,而一些需要高速存取的变量则缓存在内存中。模板的缓存把动态代码编译成静态文 件放入硬盘,不用每次访问都编译,直接读出即可。

通常来说,缓存组件都是同时结合内存和硬盘的,当内存满后,把部分数据持久化存 到硬盘,或定期dump把内存的数据写入硬盘,防止数据丟失。

缓存有三个要素:命中率、缓存更新策略、缓存最大数据量。下面分别介绍。

命中率

通常通过命中率衡量缓存机制的好坏和效率。
命中率指请求缓存次数和缓存返回正确结果次数的比例。比例越高,证明缓存的使用 率越高。缓存机制比较好理解,也很简单。以Query Cache为例。
MySQL的Query Cache用来缓存和Query相关的数据。具体来说, Query Cache缓 存 客 户 端 提 交 给 MySQL的 SELECT语 句 以 及 该 语 句 的 结 果 集 。就是将 SELECT语句和语句的结果做Hash映射关系后保存在一定的内存区域中。
MySQL提供一系列Global Status记录Query Cache当前状态,具体如下:
Qcache_free_blocks:目前处于空闲状态的Query Cache中内存block数目。 Qcache_free_memory:目前处于空闲状态的Query Cache内存总量。
Qcache_hits: Query Cache命中次数。
Qcache_inserts:向Query Cache中插入新的Query Cache次数,也就是没有 命中的次数。
Qcache_lowmem_prunes:当Query Cache内存容量不够,需要从中删除旧 的Query Cache以给新Cache对象使用的次数。
Qcache _ not_ cached:没有被Cache的SQL数,包括无法被Cache的SQL以 及由于query_
cache _ type设置而不会被Cache的SQL。
Qcache _queries _in _cache:目前在Query Cache中的SQL数量。
Qcache_total_blocks: Query Cache中总的block数量。
提示可以通过执行SHOW GLOBAL STATUS查看GLOBAL STATUS。

 

按照SQL Cache规则,只有SELECT语句被缓存。插入、删除、更新不需要进行 缓存,同时SHOW命令和存储过程(包括存储过程中的SELECT )也不会进入缓存结果 集。 Query Cache命中率计算公式如下:

[code]

hit rate=Qcache _ queries _ in _ cache/Com _ select

[/code]

正常缓存命中率是多少呢?不同缓存应用中其值大相径庭。以Query Cache为例, 经过服务器一段时间的运行和积累,其命中率通常都能达到98%以上。而对于另外一些 缓存应用,缓存命中率能达到85%以上就已经很高了,达到98%是理想状态。这跟缓存 机制的实现有很大关系。一般越复杂的缓存机制,越难保证命中率,并且命中率的提高还 需要积累和练习。随着服务器的运行和积累,缓存命中率会逐渐增长直至稳定状态。

需要注意的是,如果数据频繁更新,就需要考虑缓存的合理性。因为频繁更新会使命 中率大大降低。以Query Cache为例, Query Cache具有失效机制,如果表中的数据 变化比较频繁,大量Query Cache频繁失效,命中率就可能比较低。这种场景下, Query Cache不仅不能提高效率,反而可能造成负面影响。

缓存更新策略

MySQL的Query Cache缓存更新策略很简单。在MySQL中,可以设置Query Cache所使用的总内存,MySQL会把默认可以进行缓存的SQL语句的结果集进行缓存, —旦内存塞满后,就会剔除老的Query Cache对象。同时,为了保证Query Cache中 的内容与是实际数据绝对一致,当表中的数据有任何变化,包括新增、修改、删除等,都 会使所有弓I用到该表的SQL的Query Cache失效。

一般把缓存更新策略归纳为以下几种:

  • FIFO[First In First Out]。最先进入缓存得数据在缓存空间不够情况下(超出最大元 素限制时)会被首先清理出去。
  • LFU[Less Frequently Used]。最少使用的元素会被清理掉。这要求缓存的元素有 hit属性,在缓存空间不够得情况下,hit值最小的将会被清出缓存。
  • LRU[Least Recently Used]。最近最少使用的元素被清理。缓存的元素有一个时 间戳,当缓存容量满了,而又需要腾出地方缓存新元素时,现有缓存元素中时间戳离当前 时间最远的元素将被清出缓存。

思考MySQL的Query Cache属于什么策略?

根据前面的描述,可以判断出,MySQL没有对每_条(51^7 Cache的使用进行维 护,当内存满后,简单地清除最早的数据,那么,它应该属于FIFO策略,即队列清除。 不少缓存组件都使用了队列这种简单的策略。

缓存最大数据量

 

缓存最大数据量是在缓存中能处理元素的最大个数或所能使用的最大存储空间。通常 各种缓存机制都会对缓存最大数据量进行限定,可以是固定大小的存储空间、集合个数, 或者由操作系统所能分配和处理的存储空间决定。

例如,MySQL的Query Cache缓存最大数据量由query_cache_size参数决定, 并且可以修改。而基于内存的Key Value实施方案Memcached,其缓存最大数据量可 使用内存由操作系统决定,默认为64MB,每次最大可申请内存为2MB。

超过缓存机制所允许的最大数据量系统会进行相应处理,一般有四种处理方式:

  1. 停止缓存服务,所有缓存数据被清空。
  2. 拒绝写入,不再对缓存数据进行更新。
  3. 根据缓存更新策略清除旧数据。
  4. 在方式3基础上,将淘汰的数据备份,腾出新的空间。实际应用中,通常以方式3、方式4最为常见。

 

文件缓存

 

文件缓存是把缓存数据存储到文件系统即硬盘中。与内存相比,硬盘属于比较慢的存 储设备。那为什么还需要用到文件缓存呢?原因如下:

  • 磁盘容量大,可以存放足够多的数据。现在的常规硬盘已经进入TB级别,但内存还 处于GB级別(1TB=1024GB)。磁盘价格远远低于内存价格,通常只有同样大小内存售 价的百分之一到十分之一。
  • 磁盘与内存相比更稳定更可靠,断电后数据不丟失,存储也比较简单可靠。
  • 随着制造技术的进步,出现了固态硬盘( Solid State Disk, SSD),使硬盘的读取 和写入速度得到极大的提高,能达到500Mb/s。
  • 扩展容易。可以使用磁盘阵列、分布式处理等进行大规模的存储和管理。

由于以上几点原因,文件缓存在Web应用中常见于模板弓I擎和配置文件的处理中。

 

文件缓存机制

 

客户端缓存

 

客户端缓存规则

HTTP协议中的缓存使用

 

HTTP缓存实例

 

HTML 5中的Application Cache

 

Web服务器缓存

在Web层面上的缓存,除了基于HTTP协议加浏览器实现外,还可通过一些Web服 务器自带的缓存组件,以及服务器和浏览器之间的代理服务器提供的缓存功能实现。下面 对这类缓存做简单介绍。

Apache 缓存

 

Apache的Expires和Cache Control模块包含控制缓存的信息。这些模块需要和 Apache—起编译。虽然它们已经包含在发布版本中,但默认并没有启用。确定相应模块 已经被启用的方法是:找到httpd程序并运行httpdl会列出可用模块,要用的模块是mod 一 expires 和 mod _ headers。

Apache—旦启用了相应模块,就可以在.htaccess文件或者服务器的access.conf 文件中,通过mod _ expires设置副本过期时间了,包括设置控制应答时的Expires头内 容和Cache Control头的max age指令,代码如下:

[code]
ExpiresActive On
ExpiresByType image/gif"access plusl month"
ExpiresByType image/jpg"access plus 1 month"
ExpiresByType image/jpeg"access plusl month"
ExpiresByTypeimage/x-icon"access plusl month"
ExpiresByType image/bmp"accessplusl month"
ExpiresByTypeimage/png"accessplusl month"
ExpiresByType text/html"access plus 30 minutes"
ExpiresByType text/css"access plus 30 minutes"
ExpiresByType text/txt"access plus 30 minutes"
ExpiresByType text/js"access plus 30 minutes"
ExpiresByType application/x-javascript"access plus 30 minutes" ExpiresByType application/x-shockwave-flash"access plus 30 minutes"
[/code]

也可用以下代码进行设置:

[code]
< ifmodule mod_expires.c>
<filesmatch" \. (jpg I gif I png | css | js)$"> ExpiresActive on
ExpiresDefault"access plus 1 year"
</filesmatch>
</ifmodule >
[/code]

设置Expires后,系统会自动输出Cache Control的max age信息,关于Expires 详细内容可以查看Apache官方文档。

如果在 Windows 系统下使用 Apache , 建 议 到 http://www.apachelounge.com/download/处下载改进的Apache服务器以及相关 的缓存组件。Apache lounge是Apache的改进版,在性能、稳定性和内存管理上都超 过基于VC 6编译的Apache官方版本。Apache lounge版本随着Apache官方版本同步 更新,所以不需要担心版本和安全问题。

 

Nginx 缓存

 

Nginx ( eNginex)是高性能HTTP和反向代理服务器,也是IMAP/POP3/SMTP 代理服务器。由Igor Sysoev为Rambler.ru站点(俄罗斯访问量第二)开发,它已经在 该站点运行超过四年。Igor将源代码以类BSD许可证的形式发布。Nginx因为稳定性、丰 富的功能集、示例配置文件和低系统资源消耗而闻名。目前,国内各大门户网站已经部署 了Nginx,如新浪、网易、腾讯等;国内几个重要视频分享网站也部署了Nginx。 Nginx 技术在国内日趋火热,越来越多的网站开始部署Nginx。

Nginx体积小、配置简单、扩展性强,通过众多开源模块发挥强大的功能。Nginx性 能远远超越传统的Apache。Nginx配合PHP的FastCG膜式,充分利用PHP天生的优 势,具有极大的负载能力。

Nginx还是反向代理软件,可实现负载平衡和集群。
在Nginx中可以实现传统的缓存以及基于proxy _ cache的缓存。
从Nginx 0.7.44版开始支持类似Squid的较正规的缓存功能,但是目前还处于开发阶 段,支持相当有限。这个缓存把链接用md5编码经哈希后保存,所以支持任意链接,同时 支持404/301/302这样的非200状态。
配置一个缓存空间的代码如下:

[code]
proxy _ cache _ path/path/to/cache levels=1: 2 keys _ zone=NAME: 10m inactive=5m max size=2m clean _time=1m;
[/code]

注意,这个配置在server标签外,levels指定该缓存空间有两层hash目录,第一层 1个字母,第二层2个字母,保存的文件名类似 于/path/to/cache/c/29/b7f54b2df7773722d382f4809d65029c; keys — zone 为 这个空间起个名字,10m指空间大小为10MB; inactive的5m指缓存默认时长为5分钟; max _ size的2m指单个文件超过2M B就不缓存; clean _ time指定1分钟清理一次缓存。

[code]
location/ {
proxy_pass http: "www_linuxidc.com/;
proxy_cache NAME; #使用NAME这个keys_zone
proxy _cache _valid 200 3021h; #200和302状态码保存1小时 proxy_cache_valid 3011d; #301状态码保存1天
proxy_cache_valid any 1m; #其他的保存1分钟
}
[/code]

此外,还有基于第三方插件的缓存,例如新浪开发的针对Nginx 0.6.39及以下版本 的NCache插件,利用Nginx和Memcached实现一部分类似Squid的缓存功能。此插 件现在已经停止维护,并且在最新的Nginx稳定版本已经不兼容此插件。因Nginx集成的 proxy _cache已经能满足大部分需求,所以其成为在Nginx服务器上实现缓存机制的最 流行、最简单的选择。

还有一些代理服务器及加速器等也能实现缓存管理,如squid、Vanish等,但这类 软件和实现方案都有一定的局限性。比如Squid部署虽然比较简单,但是处理能力低;而 Vanish虽然处理能力和连接速度都很出色,但是在大负载下存在丟包问题。而Apache 模块众多,功能丰富,却损失了处理速度和负载能力。我们应该根据自己实际需求,在充 分测试的前提下,选择最合适我们的方案。

 

本章主要介绍缓存的原理,从不同层次介绍缓存的实现。书中从服务器端介绍到客户 端缓存,综合缓存各个阶段,合理运用各种缓存工具,以达到提高性能和速度,降低消耗 的目的。

缓存的本质就是在存在两种不同速率介质的情况下,利用速率较大的介质平衡性能。 因此,在开发中时刻要想到这一点,即系统中是否存在速率差,怎么利用这种速率差。这 就需要开发者拓宽自己的视野,从最前端考虑到最后端。比如,在一些大的互联网公司, 对缓存的设计已经不再停留在简单的文件缓存和内存缓存,而是深入到了CPU缓存的级 别,通过合理规划变量的内存布局,充分利用CPU的高速缓存区。

缓存的目的就是为了获得性能的提升,从实现成本的角度来说,缓存是实现成本最低 的手段。在缓存的策略的设计上,应该以缓存的三要素为参考,做到以最小的付出得到最 大的回报。本章中花了较多篇幅讲解客户端缓存,这是一种实现成本最低的方案。

发表评论

电子邮件地址不会被公开。 必填项已用*标注