php开发指南之19章:缓存简介(一)
缓存的简介
一。缓存介绍
一般情况下,一个网站,或者一个应用,它的一般形式是,浏览器请求应用服务器,应用服务器做一堆计算后再请求数据库,数据库收到请求后再作一堆计算后把数据返回给应用服务器,应用服务器再作一堆计算后把数据返回给浏览器.这个是一个标准流程.但是随着web业务的复杂和并发的增加,应用服务器和数据库服务器所做的计算也越来越多,但是往往我们的应用服务器资源是有限的,数据库每秒中接受请求的次数也是有限的.如何利用有限的资源来提供尽可能大的吞吐量呢,一个办法:减少计算量,缩短请求流程(减少网络io或者硬盘io),这时候缓存就可以大展手脚了.缓存的基本原理就是打破上面所描绘的标准流程,在这个标准流程中,任何一个环节都可以被切断.请求可以从缓存里取到数据直接返回.这样不但节省了时间,提高了响应速度,而且也节省了硬件资源.可以让我们有限的硬件资源来服务更多的用户.
在web的世界,理论上每一层都是可以被缓存的。我们从最底层说起,以php为例,有ZEND虚拟机的变量缓存,有memcache这样的key-value内存缓存,有APC,eAccelerator这样的主要基于Opcode字节码的缓存,然后到了数据库层,有数据库的SQL CACHE,再到servlet容器层,有apache的缓存,在sevlet在上去一点点,还有一个webcache层(如squid,varnish等等),然后应用级别的smarty实现的文件缓存,再往前,就到了基于HTTP协议和浏览器自身实现的浏览器缓存。当然,上面的分类中存在重复和交叉,这只是一个粗略的描述。
缓存放在什么地方?无非就是内存和硬盘。文件缓存如模板机制自然是缓存在硬盘上的,而一些需要高速存取的变量则缓存在内存中。模板的缓存是把动态的代码编译成静态文件,放入硬盘,就不用每次访问都编译了,直接读出即可。通常来说,缓存组件都是同时结合内存和硬盘,当内存满后,就把部分数据持久化到硬盘,或通过定期dump,把内存的数据写入硬盘,防止数据丢失。由于nosql技术和流行,有时候会把数据库缓存单列出来。
通常衡量缓存机制的好坏和效率,则是通过一个叫做命中率的指标来衡量的。命中率是指请求缓存次数和缓存返回正确结果次数的比例。比例越高,就证明缓存的使用率越高。我们跳跃一下,以我在前面章节提到的sql cache为例。这个缓存机制比较好理解,也很简单。
先简单介绍下mysql的query cache.
MySQL Query Cache 是用来缓存和 Query 相关的数据的。具体来说,Query Cache 缓存了我们客户端提交给 MySQL 的 SELECT 语句以及该语句的结果集。大概来讲,就是将 SELECT 语句和语句的结果做了一个 HASH 映射关系然后保存在一定的内存区域中。
使用举例:
# 强制使用缓存SELECT SQL_CACHE id,field FROM table WHERE 1
# 强制不使用缓存 SELECT SQL_NO_CACHE id,field FROM table WHERE 1
在大部分的 MySQL 分发版本中,Query Cache 功能默认都是打开的,我们可以通过调整 MySQL Server 的参数选项打开该功能。主要由以下5个参数构成:
query_cache_limit:允许 Cache 的单条 Query 结果集的最大容量,默认是1MB,超过此参数设置的 Query 结果集将不会被 Cache
query_cache_min_res_unit:设置 Query Cache 中每次分配内存的最小空间大小,也就是每个 Query 的 Cache 最小占用的内存空间大小
query_cache_size:设置 Query Cache 所使用的内存大小,默认值为0,大小必须是1024的整数倍,如果不是整数倍,MySQL 会自动调整降低最小量以达到1024的倍数
query_cache_type:控制 Query Cache 功能的开关,可以设置为0(OFF),1(ON)和2(DEMAND)三种,意义分别如下:
0(OFF):关闭 Query Cache 功能,任何情况下都不会使用 Query Cache
1(ON):开启 Query Cache 功能,但是当 SELECT 语句中使用的 SQL_NO_CACHE 提示后,将不使用Query Cache
2(DEMAND):开启 Query Cache 功能,但是只有当 SELECT 语句中使用了 SQL_CACHE 提示后,才使用 Query Cache
query_cache_wlock_invalidate:控制当有写锁定发生在表上的时刻是否先失效该表相关的 Query Cache,如果设置为 1(TRUE),则在写锁定的同时将失效该表相关的所有 Query Cache,如果设置为0(FALSE)则在锁定时刻仍然允许读取该表相关的 Query Cache。
提示:这些参数可以用下列语句查看

query_cache中存储的是和SQL语句对应的结果集,二者是通过hash进行关联的。这就是说,如果SQL语句中的大小写变了,或者表结构变了,query cache都会失效。
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. 只有 SELECT 语句被缓存。 插入、删除、更新当然不需要进行缓存了,同时SHOW命令和存储过程(包括存储过程中的SELECT)也不会进入缓存结果集。因此,我们可以写出query cache的命中率计算公式:hit rate=Qcache_queries_in_cache/Com_select(有些文章的说法可能和这里不太一致,我个人认为,这个公式更符合定义一些)更多详细的内容请看这里:http://aiyooyoo.com/index.php/archives/186/,http://aiyooyoo.com/index.php/archives/128/
上面提到了第二个概念“缓存更新策略”。
Mysql的query cache的缓存更新策略很简单。在mysql中,可以设置query cache所使用的总内存,mysql会把默认可以进行缓存的SQL语句的结果集进行缓存,一旦内存塞满后,就会剔除老的query cache对象。同时,为了保证 Query Cache 中的内容与是实际数据绝对一致,当表中的数据有任何变化,包括新增,修改,删除等,都会使所有引用到该表的 SQL 的 Query Cache 失效。
这里“数据表更改”包括: INSERT, UPDATE, DELETE, TRUNCATE, ALTER TABLE, DROP TABLE, or DROP DATABASE等。
一般地,我们把缓存更新策略归纳为以下几种:
(1)FIFO[first in first out] 最先进入缓存得数据在缓存空间不够情况下(超出最大元素限制时)会被首先清理出去 。 (2)LFU[Less Frequently Used] 一直以来最少被使用的元素会被被清理掉。这就要求缓存的元素有一个hit 属性,在缓存空间不够得情况下,hit 值最小的将会被清出缓存。
(3)LRU[Least Recently Used] 最近最少使用的,缓存的元素有一个时间戳,当缓存容量满了,而又需要腾出地方来缓存新的元素的时候,那么现有缓存元素中时间戳离当前时间最远的元素将被清出缓存。
思考下mysql的query cache属于什么策略?根据我在前面的描述,应该可以判断出,mysql并没有对每一条query cache的使用进行维护,当内存满后,是简单的清除最早的数据,那么,它应该是属于FIFO策略,即队列清除。不少缓存组件都使用了队列这种简单的策略。队列的概念上一章已经详述了,此处不提。
Shopex使用了secache缓存, Secache是一个key-value的二进制数据库,使用hash索引方式快速查找定位到相应的文件在数据文件中的位置,并且采用了拉链法解决冲突。此代码使用了LRU的缓存策略。当seCache data中的某一存储区中数据超过限制时,会根据LRU算法得出该存储区中需要淘汰的缓存文件以供新文件的存入。array('size','free','lru_head','lru_tail','hits','miss');这句代码描述的就是其slab结构。Hits描述的就是这个区块的命中率(secache把数据按大小放入不同的区块,一个区块存储的就是一个文件)。当fetch数据时,就会执行hits+1操作。当store时,判断数据块大小,存入合适的存储区(存储区有多个块),并执行缓存更新策略。不过secache的LRU和我们定义里的LRU是不太一样的,它没有使用时间戳来标记,而是通过查找lru链表来实现的(因为链表是有序的)。Secache的代码确实很不错,如果有数据结构基础的可以好好看看。
一。缓存介绍
一般情况下,一个网站,或者一个应用,它的一般形式是,浏览器请求应用服务器,应用服务器做一堆计算后再请求数据库,数据库收到请求后再作一堆计算后把数据返回给应用服务器,应用服务器再作一堆计算后把数据返回给浏览器.这个是一个标准流程.但是随着web业务的复杂和并发的增加,应用服务器和数据库服务器所做的计算也越来越多,但是往往我们的应用服务器资源是有限的,数据库每秒中接受请求的次数也是有限的.如何利用有限的资源来提供尽可能大的吞吐量呢,一个办法:减少计算量,缩短请求流程(减少网络io或者硬盘io),这时候缓存就可以大展手脚了.缓存的基本原理就是打破上面所描绘的标准流程,在这个标准流程中,任何一个环节都可以被切断.请求可以从缓存里取到数据直接返回.这样不但节省了时间,提高了响应速度,而且也节省了硬件资源.可以让我们有限的硬件资源来服务更多的用户.
在web的世界,理论上每一层都是可以被缓存的。我们从最底层说起,以php为例,有ZEND虚拟机的变量缓存,有memcache这样的key-value内存缓存,有APC,eAccelerator这样的主要基于Opcode字节码的缓存,然后到了数据库层,有数据库的SQL CACHE,再到servlet容器层,有apache的缓存,在sevlet在上去一点点,还有一个webcache层(如squid,varnish等等),然后应用级别的smarty实现的文件缓存,再往前,就到了基于HTTP协议和浏览器自身实现的浏览器缓存。当然,上面的分类中存在重复和交叉,这只是一个粗略的描述。
缓存放在什么地方?无非就是内存和硬盘。文件缓存如模板机制自然是缓存在硬盘上的,而一些需要高速存取的变量则缓存在内存中。模板的缓存是把动态的代码编译成静态文件,放入硬盘,就不用每次访问都编译了,直接读出即可。通常来说,缓存组件都是同时结合内存和硬盘,当内存满后,就把部分数据持久化到硬盘,或通过定期dump,把内存的数据写入硬盘,防止数据丢失。由于nosql技术和流行,有时候会把数据库缓存单列出来。
通常衡量缓存机制的好坏和效率,则是通过一个叫做命中率的指标来衡量的。命中率是指请求缓存次数和缓存返回正确结果次数的比例。比例越高,就证明缓存的使用率越高。我们跳跃一下,以我在前面章节提到的sql cache为例。这个缓存机制比较好理解,也很简单。
先简单介绍下mysql的query cache.
MySQL Query Cache 是用来缓存和 Query 相关的数据的。具体来说,Query Cache 缓存了我们客户端提交给 MySQL 的 SELECT 语句以及该语句的结果集。大概来讲,就是将 SELECT 语句和语句的结果做了一个 HASH 映射关系然后保存在一定的内存区域中。
使用举例:
# 强制使用缓存SELECT SQL_CACHE id,field FROM table WHERE 1
# 强制不使用缓存 SELECT SQL_NO_CACHE id,field FROM table WHERE 1
在大部分的 MySQL 分发版本中,Query Cache 功能默认都是打开的,我们可以通过调整 MySQL Server 的参数选项打开该功能。主要由以下5个参数构成:
query_cache_limit:允许 Cache 的单条 Query 结果集的最大容量,默认是1MB,超过此参数设置的 Query 结果集将不会被 Cache
query_cache_min_res_unit:设置 Query Cache 中每次分配内存的最小空间大小,也就是每个 Query 的 Cache 最小占用的内存空间大小
query_cache_size:设置 Query Cache 所使用的内存大小,默认值为0,大小必须是1024的整数倍,如果不是整数倍,MySQL 会自动调整降低最小量以达到1024的倍数
query_cache_type:控制 Query Cache 功能的开关,可以设置为0(OFF),1(ON)和2(DEMAND)三种,意义分别如下:
0(OFF):关闭 Query Cache 功能,任何情况下都不会使用 Query Cache
1(ON):开启 Query Cache 功能,但是当 SELECT 语句中使用的 SQL_NO_CACHE 提示后,将不使用Query Cache
2(DEMAND):开启 Query Cache 功能,但是只有当 SELECT 语句中使用了 SQL_CACHE 提示后,才使用 Query Cache
query_cache_wlock_invalidate:控制当有写锁定发生在表上的时刻是否先失效该表相关的 Query Cache,如果设置为 1(TRUE),则在写锁定的同时将失效该表相关的所有 Query Cache,如果设置为0(FALSE)则在锁定时刻仍然允许读取该表相关的 Query Cache。
提示:这些参数可以用下列语句查看

query_cache中存储的是和SQL语句对应的结果集,二者是通过hash进行关联的。这就是说,如果SQL语句中的大小写变了,或者表结构变了,query cache都会失效。
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. 只有 SELECT 语句被缓存。 插入、删除、更新当然不需要进行缓存了,同时SHOW命令和存储过程(包括存储过程中的SELECT)也不会进入缓存结果集。因此,我们可以写出query cache的命中率计算公式:hit rate=Qcache_queries_in_cache/Com_select(有些文章的说法可能和这里不太一致,我个人认为,这个公式更符合定义一些)更多详细的内容请看这里:http://aiyooyoo.com/index.php/archives/186/,http://aiyooyoo.com/index.php/archives/128/
上面提到了第二个概念“缓存更新策略”。
Mysql的query cache的缓存更新策略很简单。在mysql中,可以设置query cache所使用的总内存,mysql会把默认可以进行缓存的SQL语句的结果集进行缓存,一旦内存塞满后,就会剔除老的query cache对象。同时,为了保证 Query Cache 中的内容与是实际数据绝对一致,当表中的数据有任何变化,包括新增,修改,删除等,都会使所有引用到该表的 SQL 的 Query Cache 失效。
这里“数据表更改”包括: INSERT, UPDATE, DELETE, TRUNCATE, ALTER TABLE, DROP TABLE, or DROP DATABASE等。
一般地,我们把缓存更新策略归纳为以下几种:
(1)FIFO[first in first out] 最先进入缓存得数据在缓存空间不够情况下(超出最大元素限制时)会被首先清理出去 。 (2)LFU[Less Frequently Used] 一直以来最少被使用的元素会被被清理掉。这就要求缓存的元素有一个hit 属性,在缓存空间不够得情况下,hit 值最小的将会被清出缓存。
(3)LRU[Least Recently Used] 最近最少使用的,缓存的元素有一个时间戳,当缓存容量满了,而又需要腾出地方来缓存新的元素的时候,那么现有缓存元素中时间戳离当前时间最远的元素将被清出缓存。
思考下mysql的query cache属于什么策略?根据我在前面的描述,应该可以判断出,mysql并没有对每一条query cache的使用进行维护,当内存满后,是简单的清除最早的数据,那么,它应该是属于FIFO策略,即队列清除。不少缓存组件都使用了队列这种简单的策略。队列的概念上一章已经详述了,此处不提。
Shopex使用了secache缓存, Secache是一个key-value的二进制数据库,使用hash索引方式快速查找定位到相应的文件在数据文件中的位置,并且采用了拉链法解决冲突。此代码使用了LRU的缓存策略。当seCache data中的某一存储区中数据超过限制时,会根据LRU算法得出该存储区中需要淘汰的缓存文件以供新文件的存入。array('size','free','lru_head','lru_tail','hits','miss');这句代码描述的就是其slab结构。Hits描述的就是这个区块的命中率(secache把数据按大小放入不同的区块,一个区块存储的就是一个文件)。当fetch数据时,就会执行hits+1操作。当store时,判断数据块大小,存入合适的存储区(存储区有多个块),并执行缓存更新策略。不过secache的LRU和我们定义里的LRU是不太一样的,它没有使用时间戳来标记,而是通过查找lru链表来实现的(因为链表是有序的)。Secache的代码确实很不错,如果有数据结构基础的可以好好看看。
我看见别人的-10/17可以提交?
答案当然只有一个了,是我放水了