php开发指南之19章:缓存简介(三)
刚刚讲的是最简单的文件缓存,再复杂一点,讲讲稍微复杂点的opcode缓存。
所谓opcode缓存就是把php在经虚拟机把php代码编译成一种中间码(这种代码就叫opcode)的结果缓存起来(可以缓存到硬盘,也可以到内存),下一次php运行此页面时,只要直接解释这堆代码就行了。这样就省去了flex语法器进行语法编译和大部分语法检查(这个语法检查在多个阶段均存在)的过程,一定程度上提高了php的运行速度,减轻服务器的负荷。eAccelerator就是这么的一款工具,当然它的功效不仅如此。至于eAccelerator的安装和配置我在第一章就讲过了,此处不在赘述。
Opcode长啥样?我们直观的来看一下。
首先安装VLD扩展,VLD全名是Vulcan Logic Disassembler,可以用来检测PHP脚本的执行情况。
【Linux下】:安装VLD:wget http://pecl.php.net/get/vldtar zxvf vld-0.9.1.tgzcd vld-0.9.1phpize./configuremake install编辑php.ini文件激活vld扩展:extension=vld.so如果是在win下,可参考我博客的一篇文章进行编译,http://aiyooyoo.com/index.php/archives/212/。如果你不大会的话,或者觉得麻烦,也可以用我编译好的,在本指南第一章的源代码目录里有。此扩展在php5.3.3-5.3.5测试均可用,其他较早版本应该也可以。同样的,在win下安装扩展的操作不再描述。现在我们写一个文件测试一下,代码如下:
<?php echo "helloword\r";
$data['first']='Hello';
$data['first']['second']='world';echo $data ['first'];
在命令行下执行php -dvld.active=1 g:\bak\temp\tempcode\time.php,得到输出如下
filename: G:\bak\temp\tempcode\time.phpfunction name: (null)
number of ops: 13compiled vars: !0 = $data
line # * op fetch ext return operands
--------------------------------------------------------------------------------
2 0 > EXT_STMT 1 ECHO 'helloword%0D'
3 2 EXT_STMT 3 ZEND_ASSIGN_DIM !0, 'first' 4 ZEND_OP_DATA 'Hello', $1
4 5 EXT_STMT 6 FETCH_DIM_W $2 !0, 'first' 7 ZEND_ASSIGN_DIM $2, 'second' 8 ZEND_OP_DATA 'world', $4
5 9 EXT_STMT 10 FETCH_DIM_R $5 !0, 'first' 11 ECHO $5 12 > RETURN 1
branch: # 0; line: 2- 5; sop: 0; eop: 12path #1: 0,welloword
从这里我们可以看出,这段代码被分成了13小步执行。看一张C的hello world经过汇编后的代码,有没有发现上面的OPCODE和汇编代码很像。没错,opcode就是php的“汇编代码”。OpCode就是Operation Code,意即操作码的意思。注意:OpCode不是php里的专有名词。 Php里的Opcode是一种PHP脚本编译后的中间语言,就像Java的ByteCode,PHP的语言引擎Zend执行php代码时候,会把php代码经过分成token,词法分析的过程转成opcode,然后顺序执行。

而eAccelerator干的就是这种事,把opcode缓存起来,当下次请求过来时,直接调用解释好的opcode字节码,省去了大部分执行消耗。按照我在第一章提到的配置方法,配置好eAccelerator后,当你运行php后,就能在预设的缓存目录找到被eAccelerator缓存的文件了。你可以到其官方网站(http://eaccelerator.net/browser/eaccelerator/trunk/control.php)下载control.php文件,把它和源代码中的dasm.php,info.php放到一个目录下(注意:只有linux版本的源码包才有此两文件,win下拷贝一下就可以了。),并修改php的配置文件,加上如下的字段,设置允许管理员进行操作的执行路径。eaccelerator.allowed_admin_path=/var/www/html/。运行127.1/control.php,以admin/eAccelerator登陆即可在web界面下查看eAccelerator的运行信息并进行缓存管理了。如图:

eAccelerator同时也提供了一组API,可以让我们操作缓存数据。如eaccelerator_put($key, $value, $ttl=0)将 $value 存储在共享内存中,并存储 $tll 秒.eaccelerator_get($key)从共享内存中返回 eaccelerator_put() 函数所存储的缓存数值,如果不存在或者已经过期,则返回 null.更多的API借口可以参考官方文档。
现在进入第三阶段,nosql系列。Nosql不是not sql,而是not only sql,如memcached,Cassandra,mongodb,redis,tt等。Nosql同样是存储数据的一种数据库,不过其主要是基于内存和key-value(也不尽是,有基于document,也有基于XML的)。由于nosql通常是基于内存的,所以其同时还具有内存缓存的作用。故常有人困惑或争执其是缓存软件还是数据库软件。其实这个可以从其命名看出。向memcached偏向于缓存,故追求速度。Mongodb偏向于数据库,故数据类型较丰富。从名字上不能明显看出来的,其官方网站也会有个偏向性的定义。
我们讲讲最简单的memcached.Memcached是danga.com(运营LiveJournal的技术团队)开发的一套分布式内存对象缓存系统,用于在动态系统中减少数据库负载,提升性能。其特点主要有如下:
:协议简单
:基于libevent的事件处理
:内置内存存储方式
:memcached不互相通信的分布式

Memcached处理的原子是每一个(key,value)对(以下简称kv对),key会通过一个hash算法转化成hash-key,便于查找、对比以及做到尽可能的散列。同时,memcached用的是一个二级散列,通过一张大hash表来维护。Memcached有两个核心组件组成:服务端(ms)和客户端(mc),在一个memcached的查询中,mc先通过计算key的hash值来 确定kv对所处在的ms位置。当ms确定后,客户端就会发送一个查询请求给对应的ms,让它来查找确切的数据。因为这之间没有交互以及多播协议,所以 memcached交互带给网络的影响是最小化的。 当ms的hash表满了之后,新的插入数据会替代老的数据,更新的策略是LRU(最近最少使用),以及每个kv对的有效时限。 先安装memcached吧,可以参考网络上的教程。安装后启动服务。由于linux版本比较混乱,各种系统的安装方式都有所差异。大致都是先安装其依赖的libevent库,如:sudu yum install libevent libevent-del然后编译安装memcached.(源码地址:http://memcached.org/)win下的话,可以到此下载一个编译版::http://code.jellycan.com/memcached/.(win下使用仅供开发测试和学习,不宜用于部署环境。)以win为例,进入memcached所在的目录,输入memcached -h可查看帮助命令。先输入memcached -d install安装服务,然后输入memcached -d start可以启动memcache服务。可输入netstat -ano查看服务是否启动成功,默认端口是11211,可以自行更改。
下载php5.3+版的php_memcache.dll,复制到ext目录,,php.ini 加入一行extension=php_memcache.dll,重新启动Apache,然后查看一下phpinfo,如果有memcache,那么就说明安装成功。linux的话,请自行编译安装.
新建文件,输入以下代码:
<?php
$mem = new Memcache;
$mem->connect('127.0.0.1',11211);
$mem->set('key','value of key', 0, 10);
$val = $mem->get('key');
$array=array('yellow','blue','#ffffff');
$mem->set('color',$array,0,600);
var_dump($mem->get('color'));
echo $val;
$mem->delete('key');//删除key1键值的value
//$mem->flush(); //强制刷新全部缓存,即清空memcache
?>
运行,若未报错且能看到输出则说明memcached和其在php下的扩展均正常运行。注意在连接memcached时,主机名要完全一致。我们大致过一下代码。
第一行初始化一个memcached对象。 第二行进行连接,参数分别为主机名和端口。 第三行是调用API中的set方法,塞入一条数据。其中第一个参数是数据的key,用来定位一个数据,第二个参数是需要保存的数据内容,这里是一个字符串,第三个参数是一个标记,一般设置为0或者 MEMCACHE_COMPRESSED就行了,第四个参数是数据的有效期,就是说数据在这个时间内是有效的,如果过去这个时间,那么会被 Memcache服务器端清除掉这个数据,单位是秒,如果设置为0,则是永远有效,我们这里设置了60,就是一分钟有效时间。注意,key字段守卫不要包含空格,否则会被转移成_,造成数据不一致的情况。
第四行是get方法,返回已设置的key的value.
第七行是把一个数组塞到了memcached里,在第八行我们可以看到,它是可以正常工作的。
最后一行是强制刷新,即清除所有内存中的缓存数据。memcached会自动根据过期时间进行清理。为了看到效果,可以把3,5,6行的set方法注释掉,再运行一次,由于key只是注册10S的过期时间,所以再刷新后,会发现key的值为空,而数组过期时间为600秒,刷新后还会显示出来。
以上就是memcached的基本用法了。然而memcached仅仅这么用,那就是暴殄天物了呀。memcached更常用的是分布式部署,在多台服务器上放置缓存数据,供前端查询。通常我们会在前端和数据库之间构造一个data cache层,用来避免前端对数据库的直接访问,而让所有的访问在cache层逗留。在这种部署模式下,需要注意的就是不要让前端的访问请求穿透cache,直接访问数据库,这样有可能造成数据库压力过大,导致雪崩现象。这就要注意架构的设计,提高缓存命中率了。
简单的说,我们要把数据放到cache里,可以这么做:(伪代码)
$sql="select user.* from user"
//连接memcached
$key=md5($sql);
if ( !($datas = $mc->get($key)) ) {
//在 memcached中未获取到缓存数据,则使用数据库查询获取记录集。
//mysql->fetch(&data)
// 将数据库中获取到的结果集数据保存到 memcached 中,以供下次访问时使用。
$mc->add($key, $data);
}
但是,这里又引出了一个问题,我mysql里的数据更新了,怎么通知memcached同步更新,避免读取到脏数据呢?这就需要使用mysql的UDF了。可以使用各种开源项目,如Memcached Functions for MySQL来实现你的需求。 UDF:User-Defined Function. 开发者可以对MySQL Server进行扩充,给MySQL Server增加自己特殊的功能。所有增加的功能都是通过一个简单的函数来实现。当把此方法增加到MySQL Server中后,该方法的使用方式就和MySQL Server内嵌的方法完全一样了。通过此种方式,我们能够完成我们想要的任意功能。通常,这是使用C语言编程实现的。https://launchpad.net/memcached-udfs这里就有一个项目,实现mysql和memcached的同步。
我个人更喜欢mongodb,其速度快,数据类型丰富,官方文档详细,性能高,值得关注。这一小节内容暂告一段落,下面我们继续。
所谓opcode缓存就是把php在经虚拟机把php代码编译成一种中间码(这种代码就叫opcode)的结果缓存起来(可以缓存到硬盘,也可以到内存),下一次php运行此页面时,只要直接解释这堆代码就行了。这样就省去了flex语法器进行语法编译和大部分语法检查(这个语法检查在多个阶段均存在)的过程,一定程度上提高了php的运行速度,减轻服务器的负荷。eAccelerator就是这么的一款工具,当然它的功效不仅如此。至于eAccelerator的安装和配置我在第一章就讲过了,此处不在赘述。
Opcode长啥样?我们直观的来看一下。
首先安装VLD扩展,VLD全名是Vulcan Logic Disassembler,可以用来检测PHP脚本的执行情况。
【Linux下】:安装VLD:wget http://pecl.php.net/get/vldtar zxvf vld-0.9.1.tgzcd vld-0.9.1phpize./configuremake install编辑php.ini文件激活vld扩展:extension=vld.so如果是在win下,可参考我博客的一篇文章进行编译,http://aiyooyoo.com/index.php/archives/212/。如果你不大会的话,或者觉得麻烦,也可以用我编译好的,在本指南第一章的源代码目录里有。此扩展在php5.3.3-5.3.5测试均可用,其他较早版本应该也可以。同样的,在win下安装扩展的操作不再描述。现在我们写一个文件测试一下,代码如下:
<?php echo "helloword\r";
$data['first']='Hello';
$data['first']['second']='world';echo $data ['first'];
在命令行下执行php -dvld.active=1 g:\bak\temp\tempcode\time.php,得到输出如下
filename: G:\bak\temp\tempcode\time.phpfunction name: (null)
number of ops: 13compiled vars: !0 = $data
line # * op fetch ext return operands
--------------------------------------------------------------------------------
2 0 > EXT_STMT 1 ECHO 'helloword%0D'
3 2 EXT_STMT 3 ZEND_ASSIGN_DIM !0, 'first' 4 ZEND_OP_DATA 'Hello', $1
4 5 EXT_STMT 6 FETCH_DIM_W $2 !0, 'first' 7 ZEND_ASSIGN_DIM $2, 'second' 8 ZEND_OP_DATA 'world', $4
5 9 EXT_STMT 10 FETCH_DIM_R $5 !0, 'first' 11 ECHO $5 12 > RETURN 1
branch: # 0; line: 2- 5; sop: 0; eop: 12path #1: 0,welloword
从这里我们可以看出,这段代码被分成了13小步执行。看一张C的hello world经过汇编后的代码,有没有发现上面的OPCODE和汇编代码很像。没错,opcode就是php的“汇编代码”。OpCode就是Operation Code,意即操作码的意思。注意:OpCode不是php里的专有名词。 Php里的Opcode是一种PHP脚本编译后的中间语言,就像Java的ByteCode,PHP的语言引擎Zend执行php代码时候,会把php代码经过分成token,词法分析的过程转成opcode,然后顺序执行。

而eAccelerator干的就是这种事,把opcode缓存起来,当下次请求过来时,直接调用解释好的opcode字节码,省去了大部分执行消耗。按照我在第一章提到的配置方法,配置好eAccelerator后,当你运行php后,就能在预设的缓存目录找到被eAccelerator缓存的文件了。你可以到其官方网站(http://eaccelerator.net/browser/eaccelerator/trunk/control.php)下载control.php文件,把它和源代码中的dasm.php,info.php放到一个目录下(注意:只有linux版本的源码包才有此两文件,win下拷贝一下就可以了。),并修改php的配置文件,加上如下的字段,设置允许管理员进行操作的执行路径。eaccelerator.allowed_admin_path=/var/www/html/。运行127.1/control.php,以admin/eAccelerator登陆即可在web界面下查看eAccelerator的运行信息并进行缓存管理了。如图:

eAccelerator同时也提供了一组API,可以让我们操作缓存数据。如eaccelerator_put($key, $value, $ttl=0)将 $value 存储在共享内存中,并存储 $tll 秒.eaccelerator_get($key)从共享内存中返回 eaccelerator_put() 函数所存储的缓存数值,如果不存在或者已经过期,则返回 null.更多的API借口可以参考官方文档。
现在进入第三阶段,nosql系列。Nosql不是not sql,而是not only sql,如memcached,Cassandra,mongodb,redis,tt等。Nosql同样是存储数据的一种数据库,不过其主要是基于内存和key-value(也不尽是,有基于document,也有基于XML的)。由于nosql通常是基于内存的,所以其同时还具有内存缓存的作用。故常有人困惑或争执其是缓存软件还是数据库软件。其实这个可以从其命名看出。向memcached偏向于缓存,故追求速度。Mongodb偏向于数据库,故数据类型较丰富。从名字上不能明显看出来的,其官方网站也会有个偏向性的定义。
我们讲讲最简单的memcached.Memcached是danga.com(运营LiveJournal的技术团队)开发的一套分布式内存对象缓存系统,用于在动态系统中减少数据库负载,提升性能。其特点主要有如下:
:协议简单
:基于libevent的事件处理
:内置内存存储方式
:memcached不互相通信的分布式

Memcached处理的原子是每一个(key,value)对(以下简称kv对),key会通过一个hash算法转化成hash-key,便于查找、对比以及做到尽可能的散列。同时,memcached用的是一个二级散列,通过一张大hash表来维护。Memcached有两个核心组件组成:服务端(ms)和客户端(mc),在一个memcached的查询中,mc先通过计算key的hash值来 确定kv对所处在的ms位置。当ms确定后,客户端就会发送一个查询请求给对应的ms,让它来查找确切的数据。因为这之间没有交互以及多播协议,所以 memcached交互带给网络的影响是最小化的。 当ms的hash表满了之后,新的插入数据会替代老的数据,更新的策略是LRU(最近最少使用),以及每个kv对的有效时限。 先安装memcached吧,可以参考网络上的教程。安装后启动服务。由于linux版本比较混乱,各种系统的安装方式都有所差异。大致都是先安装其依赖的libevent库,如:sudu yum install libevent libevent-del然后编译安装memcached.(源码地址:http://memcached.org/)win下的话,可以到此下载一个编译版::http://code.jellycan.com/memcached/.(win下使用仅供开发测试和学习,不宜用于部署环境。)以win为例,进入memcached所在的目录,输入memcached -h可查看帮助命令。先输入memcached -d install安装服务,然后输入memcached -d start可以启动memcache服务。可输入netstat -ano查看服务是否启动成功,默认端口是11211,可以自行更改。
下载php5.3+版的php_memcache.dll,复制到ext目录,,php.ini 加入一行extension=php_memcache.dll,重新启动Apache,然后查看一下phpinfo,如果有memcache,那么就说明安装成功。linux的话,请自行编译安装.
新建文件,输入以下代码:
<?php
$mem = new Memcache;
$mem->connect('127.0.0.1',11211);
$mem->set('key','value of key', 0, 10);
$val = $mem->get('key');
$array=array('yellow','blue','#ffffff');
$mem->set('color',$array,0,600);
var_dump($mem->get('color'));
echo $val;
$mem->delete('key');//删除key1键值的value
//$mem->flush(); //强制刷新全部缓存,即清空memcache
?>
运行,若未报错且能看到输出则说明memcached和其在php下的扩展均正常运行。注意在连接memcached时,主机名要完全一致。我们大致过一下代码。
第一行初始化一个memcached对象。 第二行进行连接,参数分别为主机名和端口。 第三行是调用API中的set方法,塞入一条数据。其中第一个参数是数据的key,用来定位一个数据,第二个参数是需要保存的数据内容,这里是一个字符串,第三个参数是一个标记,一般设置为0或者 MEMCACHE_COMPRESSED就行了,第四个参数是数据的有效期,就是说数据在这个时间内是有效的,如果过去这个时间,那么会被 Memcache服务器端清除掉这个数据,单位是秒,如果设置为0,则是永远有效,我们这里设置了60,就是一分钟有效时间。注意,key字段守卫不要包含空格,否则会被转移成_,造成数据不一致的情况。
第四行是get方法,返回已设置的key的value.
第七行是把一个数组塞到了memcached里,在第八行我们可以看到,它是可以正常工作的。
最后一行是强制刷新,即清除所有内存中的缓存数据。memcached会自动根据过期时间进行清理。为了看到效果,可以把3,5,6行的set方法注释掉,再运行一次,由于key只是注册10S的过期时间,所以再刷新后,会发现key的值为空,而数组过期时间为600秒,刷新后还会显示出来。
以上就是memcached的基本用法了。然而memcached仅仅这么用,那就是暴殄天物了呀。memcached更常用的是分布式部署,在多台服务器上放置缓存数据,供前端查询。通常我们会在前端和数据库之间构造一个data cache层,用来避免前端对数据库的直接访问,而让所有的访问在cache层逗留。在这种部署模式下,需要注意的就是不要让前端的访问请求穿透cache,直接访问数据库,这样有可能造成数据库压力过大,导致雪崩现象。这就要注意架构的设计,提高缓存命中率了。
简单的说,我们要把数据放到cache里,可以这么做:(伪代码)
$sql="select user.* from user"
//连接memcached
$key=md5($sql);
if ( !($datas = $mc->get($key)) ) {
//在 memcached中未获取到缓存数据,则使用数据库查询获取记录集。
//mysql->fetch(&data)
// 将数据库中获取到的结果集数据保存到 memcached 中,以供下次访问时使用。
$mc->add($key, $data);
}
但是,这里又引出了一个问题,我mysql里的数据更新了,怎么通知memcached同步更新,避免读取到脏数据呢?这就需要使用mysql的UDF了。可以使用各种开源项目,如Memcached Functions for MySQL来实现你的需求。 UDF:User-Defined Function. 开发者可以对MySQL Server进行扩充,给MySQL Server增加自己特殊的功能。所有增加的功能都是通过一个简单的函数来实现。当把此方法增加到MySQL Server中后,该方法的使用方式就和MySQL Server内嵌的方法完全一样了。通过此种方式,我们能够完成我们想要的任意功能。通常,这是使用C语言编程实现的。https://launchpad.net/memcached-udfs这里就有一个项目,实现mysql和memcached的同步。
我个人更喜欢mongodb,其速度快,数据类型丰富,官方文档详细,性能高,值得关注。这一小节内容暂告一段落,下面我们继续。
mongodb,用过就放不下了。真是很喜欢。不过用mongodb的时候还是晕晕的,博主说的官方文档详细指的是英文文档吗?我一直硬着头皮看,看的辛苦。。。