发新话题
打印

php开发memcached案例分析

php开发memcached案例分析

例子(PHP的Memcache):

(1)

$mem = new Memcache; $mem->connect("192.168.x.y", 11211)or die ("Could not connect"); $mem->set('key', 'This is a test!', 0, 60); $val = $mem->get('key'); echo $val; ?> (2) < ?php //连接 $mem = new Memcache; $mem->connect("192.168.0.200", 12000); //保存数据 $mem->set('key1', 'This is first value', 0, 60); $val = $mem->get('key1'); echo "Get key1 value: " . $val ."
"; //替换数据 $mem->replace('key1', 'This is replace value', 0, 60); $val = $mem->get('key1'); echo "Get key1 value: " . $val . "
"; //保存数组 $arr = array('aaa', 'bbb', 'ccc', 'ddd'); $mem->set('key2', $arr, 0, 60); $val2 = $mem->get('key2'); echo "Get key2 value: "; print_r($val2); echo "
"; //删除数据 $mem->delete('key1'); $val = $mem->get('key1'); echo "Get key1 value: " . $val . "
"; //清除所有数据 $mem->flush(); $val2 = $mem->get('key2'); echo "Get key2 value: "; print_r($val2); echo "
"; //关闭连接 $mem->close(); ?>
如果正常的话,浏览器将输出: Get key1 value: This is first value Get key1 value: This is replace value Get key2 value: Array ( [0] => aaa [1] => bbb [2] => ccc [3] => ddd ) Get key1 value: Get key2 value: 程序代码分析 初始化一个Memcache的对象: $mem = new Memcache; 连接到我们的Memcache服务器端,第一个参数是服务器的IP地址,也可以是主机名,第二个参数是Memcache的开放的端口: $mem->connect("192.168.0.200", 12000); 保存一个数据到Memcache服务器上,第一个参数是数据的key,用来定位一个数据,第二个参数是需要保存的数据内容,这里是一个字符串,第三 个参数是一个标记,一般设置为0或者MEMCACHE_COMPRESSED就行了,第四个参数是数据的有效期,就是说数据在这个时间内是有效的,如果过去这个时间,那么会被Memcache服务器端清除掉这个数据,单位是秒,如果设置为0,则是永远有效,我们这里设置了60,就是一分钟有效时间: $mem->set(‘key1‘, ‘This is first value’, 0, 60); 从Memcache服务器端获取一条数据,它只有一个参数,就是需要获取数据的key,我们这里是上一步设置的key1,现在获取这个数据后输出输出: $val = $mem->get(’key1′); echo "Get key1 value: " . $val; 现在是使用replace方法来替换掉上面key1的值,replace方法的参数跟set是一样的,不过第一个参数key1是必须是要替换数据内容的key,最后输出了: $mem->replace(‘key1′, ‘This is replace value’, 0, 60); $val = $mem->get(‘key1′); echo "Get key1 value: " . $val; 同样的,Memcache也是可以保存数组的,下面是在Memcache上面保存了一个数组,然后获取回来并输出 $arr = array(‘aaa’, ‘bbb’, ‘ccc’, ‘ddd’); $mem->set(‘key2′, $arr, 0, 60); $val2 = $mem->get(‘key2′); print_r($val2); 现在删除一个数据,使用delte接口,参数就是一个key,然后就能够把Memcache服务器这个key的数据删除,最后输出的时候没有结果 $mem->delete(‘key1′); $val = $mem->get(‘key1′); echo "Get key1 value: " . $val . "
";
最后我们把所有的保存在Memcache服务器上的数据都清除,会发现数据都没有了,最后输出key2的数据为空,最后关闭连接 $mem->flush(); $val2 = $mem->get(‘key2′); echo "Get key2 value: "; print_r($val2); echo "
";
(3)数据 mysql> select uid,username,password,gender from pw_members order by uid desc limit 10; +--------+-----------+----------------------------------+--------+ | uid | username | password | gender | +--------+-----------+----------------------------------+--------+ | 751490 | a62986233 | 8d03f56ad2c48d494cd4b73b3ba64dca | 0 | | 751489 | hldhoxbh | 25d55ad283aa400af464c76d713c07ad | 0 | | 751488 | wuhan0088 | ba2206cbae7c1bfe33a54ff161943bab | 0 | | 751487 | anrron | b206e95a4384298962649e58dc7b39d4 | 0 | | 751486 | hldjxlkx | 9c98df872d24244696c393a1d26ab749 | 0 | | 751485 | 1394afjh | 25d55ad283aa400af464c76d713c07ad | 0 | | 751484 | yesi808 | 32baeaa3c422413843b015919c0be999 | 0 | | 751483 | IDC010pw | 25f9e794323b453885f5181f1b624d0b | 0 | | 751482 | ebay360v | a36b9e764318d31b4810d7d18096e6e7 | 0 | | 751481 | ppgqsvgv | 9c98df872d24244696c393a1d26ab749 | 0 | +--------+-----------+----------------------------------+--------+ 10 rows in set (0.00 sec) <?php #数据库连接信息 $host="192.168.0.71"; $user="askwan"; $passwd="passwd"; $db="pwbbs"; $query="select UID,username,password,gender from pw_members order by uid desc limit 10;"; #我这里选用了MD5方式加密SQL做为key ,也可用其他加密方式,如Base64等 $m_key=md5($query); $mem=new Memcache(); $mem->connect("192.168.0.72",11211); if(!$result=$mem->get($m_key)){ echo "这是从数据库读出来的结果!"; $connection=mysql_connect($host,$user,$passwd) or die ("Unable to conect!" ); mysql_select_db($db); $result=mysql_query($query) or die("Error query!".mysql_error()); while($row=mysql_fetch_row($result)){ $memdata[]=$row; } $mem->add($m_key,$memdata); mysql_free_result($result); mysql_close($connection); } else{ echo "这是从Memcached Server读出来的结果!\n"; } $result=$mem->get($m_key); #显示获取的数据 echo ""; echo ""; echo ""; echo ""; echo ""; echo ""; echo ""; for($i=0;$i<10;$i++){ echo ""; for($j=0;$j<4;$j++){ echo ""; } echo ""; } ?>第二种,可以避开扩展、重新编译所带来的麻烦,那就是直接使用 php-memcached-client.php,但效率会比扩展库稍差一些。 首先 下载 memcached-client.php(http://www.nioxiao.com/wp-conten ... emcached-client.zip),在下载了 memcached-client.php 之后,就可以通过这个文件中的类“memcached”对 memcached 服务进行操作了。其实代码调用非常简单,主要会用到的方法有 add()、get()、replace() 和 delete(),方法说明如下: add ($key, $val, $exp = 0) 往 memcached 中写入对象,$key 是对象的唯一标识符,$val 是写入的对象数据,$exp 为过期时间,单位为秒,默认为不限时间; get ($key) 从 memcached 中获取对象数据,通过对象的唯一标识符 $key 获取; replace ($key, $value, $exp=0) 使用 $value 替换 memcached 中标识符为 $key 的对象内容,参数与 add() 方法一样,只有 $key 对象存在的情况下才会起作用; delete ($key, $time = 0) 删除 memcached 中标识符为 $key 的对象,$time 为可选参数,表示删除之前需要等待多长时间。 下面是一段简单的测试代码,代码中对标识符为 'mykey' 的对象数据进行存取操作: // 包含 memcached 类文件require_once('memcached-client.php');// 选项设置$options = array( 'servers' => array('192.168.10.215:11211'), //memcached 服务的地址、端口,可用多个数组元素表示多个 memcached 服务 'debug' => false, //是否打开 debug 'compress_threshold' => 10240, //超过多少字节的数据时进行压缩 'persistant' => false //是否使用持久连接 );// 创建 memcached 对象实例$mc = new memcached($options);// 设置此脚本使用的唯一标识符$key = 'mykey';// 往 memcached 中写入对象$mc->add($key, 'some random strings');$val = $mc->get($key);echo "
".str_pad('$mc->add() ', 60, '_')."
";
var_dump($val);// 替换已写入的对象数据值$mc->replace($key, array('some'=>'haha', 'array'=>'xxx'));$val = $mc->get($key);echo "
".str_pad('$mc->replace() ', 60, '_')."
";
var_dump($val);// 删除 memcached 中的对象$mc->delete($key);$val = $mc->get($key);echo "
".str_pad('$mc->delete() ', 60, '_')."
";
var_dump($val);?>是不是很简单,在实际应用中,通常会把数据库查询的结果集保存到 memcached 中,下次访问时直接从 memcached 中获取,而不再做数据库查询操作,这样可以在很大程度上减轻数据库的负担。通常会将 SQL 语句 md5() 之后的值作为唯一标识符 key。下边是一个利用 memcached 来缓存数据库查询结果集的示例(此代码片段紧接上边的示例代码): $sql = 'SELECT * FROM users';$key = md5($sql); //memcached 对象标识符if ( !($datas = $mc->get($key)) ) { // 在 memcached 中未获取到缓存数据,则使用数据库查询获取记录集。 echo "
"
.str_pad('Read datas from MySQL.', 60, '_')."
"
; $conn = mysql_connect('localhost', 'test', 'test'); mysql_select_db('test'); $result = mysql_query($sql); while ($row = mysql_fetch_object($result)) $datas[] = $row; // 将数据库中获取到的结果集数据保存到 memcached 中,以供下次访问时使用。 $mc->add($key, $datas);} else { echo "
"
.str_pad('Read datas from memcached.', 60, '
'
)."n";}var_dump($datas);?>可以看出,使用 memcached 之后,可以减少数据库连接、查询操作,数据库负载下来了,脚本的运行速度也提高了。 Memcache的使用 使用Memcache的网站一般流量都是比较大的,为了缓解数据库的压力,让Memcache作为一个缓存区域,把部分信息保存在内存中,在前端能够迅速的进行存取。那么一般的焦点就是集中在如何分担数据库压力和进行分布式,毕竟单台Memcache的内存容量的有限的。我这里简单提出我的个人看法,未经 实践,权当参考。 分布式应用 Memcache本来支持分布式,我们客户端稍加改造,更好的支持。我们的key可以适当进行有规律的封装,比如以user为主的网站来说,每个用户都有 User ID,那么可以按照固定的ID来进行提取和存取,比如1开头的用户保存在第一台Memcache服务器上,以2开头的用户的数据保存在第二胎 Mecache服务器上,存取数据都先按照User ID来进行相应的转换和存取。 但是这个有缺点,就是需要对User ID进行判断,如果业务不一致,或者其他类型的应用,可能不是那么合适,那么可以根据自己的实际业务来进行考虑,或者去想更合适的方法。 减少数据库压力 这个算是比较重要的,所有的数据基本上都是保存在数据库当中的,每次频繁的存取数据库,导致数据库性能极具下降,无法同时服务更多的用户,比如 MySQL,特别频繁的锁表,那么让Memcache来分担数据库的压力吧。我们需要一种改动比较小,并且能够不会大规模改变前端的方式来进行改变目前的架构。 我考虑的一种简单方法: 后端的数据库操作模块,把所有的Select操作提取出来(update/delete/insert不管),然后把对应的SQL进行相应的hash算法 计算得出一个hash数据key(比如MD5或者SHA),然后把这个key去Memcache中查找数据,如果这个数据不存在,说明还没写入到缓存中,那么从数据库把数据提取出来,一个是数组类格式,然后把数据在set到Memcache中,key就是这个SQL的hash值,然后相应的设置一个失效时 间,比如一个小时,那么一个小时中的数据都是从缓存中提取的,有效减少数据库的压力。缺点是数据不实时,当数据做了修改以后,无法实时到前端显示,并且还有可能对内存占用比较大,毕竟每次select出来的数据数量可能比较巨大,这个是需要考虑的因素。 Memcache的安全 我们上面的Memcache服务器端都是直接通过客户端连接后直接操作,没有任何的验证过程,这样如果服务器是直接暴露在互联网上的话是比较危险,轻则数 据泄露被其他无关人员查看,重则服务器被入侵,因为Mecache是以root权限运行的,况且里面可能存在一些我们未知的bug或者是缓冲区溢出的情况,这些都是我们未知的,所以危险性是可以预见的。为了安全起见,我做两点建议,能够稍微的防止黑客的入侵或者数据的泄露。 内网访问 最好把两台服务器之间的访问是内网形态的,一般是Web服务器跟Memcache服务器之间。普遍的服务器都是有两块网卡,一块指向互联网,一块指向内 网,那么就让Web服务器通过内网的网卡来访问Memcache服务器,我们Memcache的服务器上启动的时候就监听内网的IP地址和端口,内网间的访问能够有效阻止其他非法的访问。 # memcached -d -m 1024 -u root -l 192.168.0.200 -p 11211 -c 1024 -P /tmp/memcached.pid Memcache服务器端设置监听通过内网的192.168.0.200的ip的11211端口,占用1024MB内存,并且允许最大1024个并发连接 设置防火墙 防火墙是简单有效的方式,如果却是两台服务器都是挂在网的,并且需要通过外网IP来访问Memcache的话,那么可以考虑使用防火墙或者代理程序来过滤非法访问。 一般我们在Linux下可以使用iptables或者FreeBSD下的ipfw来指定一些规则防止一些非法的访问,比如我们可以设置只允许我们的Web服务器来访问我们Memcache服务器,同时阻止其他的访问。 # iptables -F # iptables -P INPUT DROP # iptables -A INPUT -p tcp -s 192.168.0.2 ?dport 11211 -j ACCEPT # iptables -A INPUT -p udp -s 192.168.0.2 ?dport 11211 -j ACCEPT 上面的iptables规则就是只允许192.168.0.2这台Web服务器对Memcache服务器的访问,能够有效的阻止一些非法访问,相应的也可以增加一些其他的规则来加强安全性,这个可以根据自己的需要来做。 参考(1) Linux下的Memcache安装最近在研究怎么让Discuz!去应用Memcache去做一些事情,记录下Memcache安装的过程。 Linux下Memcache服务器端的安装 服务器端主要是安装memcache服务器端,目前的最新版本是 memcached-1.3.0 。 下载:http://www.danga.com/memcached/dist/memcached-1.2.2.tar.gz 另外,Memcache用到了libevent这个库用于Socket的处理,所以还需要安装libevent,libevent的最新版本是libevent-1.3。(如果你的系统已经安装了libevent,可以不用安装) 官网:http://www.monkey.org/~provos/libevent/ 下载:http://www.monkey.org/~provos/libevent-1.3.tar.gz 用wget指令直接下载这两个东西.下载回源文件后。 1.先安装libevent。这个东西在配置时需要指定一个安装路径,即./configure ?prefix=/usr;然后make;然后make install; 2.再安装memcached,只是需要在配置时需要指定libevent的安装路径即./configure ?with-libevent=/usr;然后make;然后make install; 这样就完成了Linux下Memcache服务器端的安装。详细的方法如下: 1.分别把memcached和libevent下载回来,放到 /tmp 目录下: # cd /tmp # wget http://www.danga.com/memcached/dist/memcached-1.2.0.tar.gz # wget http://www.monkey.org/~provos/libevent-1.2.tar.gz 2.先安装libevent: # tar zxvf libevent-1.2.tar.gz # cd libevent-1.2 # ./configure ?prefix=/usr # make # make install 3.测试libevent是否安装成功: # ls -al /usr/lib | grep libevent lrwxrwxrwx 1 root root 21 11?? 12 17:38 libevent-1.2.so.1 -> libevent-1.2.so.1.0.3 -rwxr-xr-x 1 root root 263546 11?? 12 17:38 libevent-1.2.so.1.0.3 -rw-r?r? 1 root root 454156 11?? 12 17:38 libevent.a -rwxr-xr-x 1 root root 811 11?? 12 17:38 libevent.la lrwxrwxrwx 1 root root 21 11?? 12 17:38 libevent.so -> libevent-1.2.so.1.0.3 还不错,都安装上了。 4.安装memcached,同时需要安装中指定libevent的安装位置: # cd /tmp # tar zxvf memcached-1.2.0.tar.gz # cd memcached-1.2.0 # ./configure ?with-libevent=/usr # make # make install 如果中间出现报错,请仔细检查错误信息,按照错误信息来配置或者增加相应的库或者路径。 安装完成后会把memcached放到 /usr/local/bin/memcached , 5.测试是否成功安装memcached: # ls -al /usr/local/bin/mem* -rwxr-xr-x 1 root root 137986 11?? 12 17:39 /usr/local/bin/memcached -rwxr-xr-x 1 root root 140179 11?? 12 17:39 /usr/local/bin/memcached-debug 安装Memcache的PHP扩展 1.在http://pecl.php.net/package/memcache 选择相应想要下载的memcache版本。 2.安装PHP的memcache扩展 tar vxzf memcache-2.2.1.tgz cd memcache-2.2.1 /usr/local/php/bin/phpize ./configure ?enable-memcache ?with-php-config=/usr/local/php/bin/php-config ?with-zlib-dir make make install 3.上述安装完后会有类似这样的提示: Installing shared extensions: /usr/local/php/lib/php/extensions/no-debug-non-zts-2007xxxx/ 4.把php.ini中的extension_dir = “./”修改为 extension_dir = “/usr/local/php/lib/php/extensions/no-debug-non-zts-2007xxxx/” 5.添加一行来载入memcache扩展:extension=memcache.so memcached的基本设置: 1.启动Memcache的服务器端: # /usr/local/bin/memcached -d -m 10 -u root -l 192.168.0.200 -p 12000 -c 256 -P /tmp/memcached.pid -d选项是启动一个守护进程, -m是分配给Memcache使用的内存数量,单位是MB,我这里是10MB, -u是运行Memcache的用户,我这里是root, -l是监听的服务器IP地址,如果有多个地址的话,我这里指定了服务器的IP地址192.168.0.200, -p是设置Memcache监听的端口,我这里设置了12000,最好是1024以上的端口, -c选项是最大运行的并发连接数,默认是1024,我这里设置了256,按照你服务器的负载量来设定, -P是设置保存Memcache的pid文件,我这里是保存在 /tmp/memcached.pid, 2.如果要结束Memcache进程,执行: # kill `cat /tmp/memcached.pid` 也可以启动多个守护进程,不过端口不能重复。 3.重启apache,service httpd restart Memcache环境测试: 运行下面的php文件,如果有输出This is a test!,就表示环境搭建成功。开始领略Memcache的魅力把! < ?php $mem = new Memcache; $mem->connect(”127.0.0.1″, 11211); $mem->set(’key’, ‘This is a test!’, 0, 60); $val = $mem->get(’key’); echo $val; ?>
Uid Username PassWord Gender
".$result[$i][$j]."

TOP