首页 > 代码库 > 扫盲:php session缓存至memcached中的方法

扫盲:php session缓存至memcached中的方法

memcached是一套分布式的快取系统,当初是Danga Interactive为了LiveJournal所发展的,但被许多软件(如MediaWiki)所使用。这是一套开放源代码软件,以BSD license授权协议发布。[1]

memcached仅支持一些非常简单的命令 比如get(获取某个键值) set(用来设定或保存一个缓存);

其本身是缓存服务器,但本身无法决定缓存任何数据,其缓存机制依赖于服务端和客户端两者必不可少(存储是由服务端进行存储,但存储什么是由客户端进行决定的)

因此客户端要自己提供缓存的键名以及时长、标志位、整个数据大小等等

 

例如:只存储hello 但只存储60秒

set key 5 60 hello

并告知服务器端,这样存储过了60秒后,由服务端进行清除数据

但是其工作机制非常独特,其缓存功能是基于Lazy模型的:

只要空间未满则不清理

 

那么问题来了:如果空间过小,而需缓存的内容过大的话,那么导致缓存抖动非常严重,存完即清理其次再去缓存这样会导致命中率下降,而毫无意义

有些时候,有些数据管理不善有可能导致缓存崩溃等

如果memcached崩溃仅导致业务层的影响,最多是速度降低 而不会导致数据层

 

memcached如何实现缓存的

memcached通过内存进行缓存数据,但并不实现持久缓存

 

存数数据的下限:

最小为48字节

最大不能超过1MB

 

但存储的数据大小有可能不一致,比如:

index.html10k

test.jpg34k

 

那memcached如何在内存中管理缓存数据

 

假如我们分别存储不同大小的数据以上为例

很显然只要分配一个足够大的空间就可以了,但是在内存中去找对应的数据我们必须要有对应的缓存对象的边界(起始存储位地址和结束存储位地址)将其当做独立的单位来管理

等其缓存失效了,空间会被腾出,时间久了可能会带来碎片,因为存储的都是非常小的数据单元,按理说如果再想高速利用则会困难,所以在这种机制下memcached的存储数据 查询数据等操作都是非常缓慢的

由此,不停快速基于内存的申请、释放反复操作,这种释放本身也消耗大量的资源和时间

因此我们需要一种高效的机制来解决内存的创建和释放的问题

对于memcached来讲首要必须解决这类内存碎片问题,不然由于内存的碎片导致进程运行的非常缓慢

 

在linux内核中引入了两种机制避免内存碎片

 

1.buddy system    伙伴系统

为了实现整个内存中以页面方式管理内存的时候有足够大的连续内存空间可用的,在物理内存中,事实上内存的管理和分配在内核级别通常以页面方式分配和使用的

通常是4k大小一个页面,buddy就是为了将这些零碎的、空闲的合并成一个连续的大的内存空间,这样就避免了页面之间产生碎片的,因此,其主要目的是为了避免内存外碎片

 

2.slab allocator  slab分配器

实现将存储小于页面单位的非常小的数据内存结构的时候之前事先分配并随时等待有需求的进程或要存储的对象使用,当我们使用之后它也不会自动消毁结构而是随时重复使用

避免内存内部碎片

 

最新版本的memcached使用的是增长因子(growth factor)来明确定义起始点开始依次增长

比如:

我们定义增长因子为其2倍

我们存储一个单位为48bytes,那么会分配其48*2 = 96bytes

如果增长因子为1.1倍

那么48+48*1.1

一旦存储空间满了,则会清理,没有存满则不会清理数据

 

memcached也支持分布式缓存基础概念

memcached也支持分布式缓存,但是彼此之间不会通信,但是一个memcached也不会缓存过多数据,如果将来缓存的数据很大的话,那只能使用多台memcached提供服务

假设这么一个场景:

前端的应用服务器很多,这些服务器角色都需要连接至数据库,为了加速数据库的性能,可对其提供memcached服务器,经过一段时间观察,我们的memcached数据库缓存过多,请求量过大一台机器根本扛不住,这时需要对其扩容。

于是我们又加入了一台memcached,但是memcached服务器之间是不能通信的

所以某一应用服务器A 要缓存数据不能只盯一台memcached,所以我们要让其轮流来提供服务,但是对memcached而言它的分布式算法是在应用程序(客户端)中实现的,而不是取决于服务器自身

 

客户端是如何分布式调度服务端

需要一定分布式算法,和调度器

(如果memcached非常多的话,完全可以使用第三方调度器进行调度,比如nginx+持久连接)

 

或一致性哈希算法:

参考:http://blog.csdn.net/kongqz/article/details/6695417

 

总结:

1、在内存中缓存

2、内存要使用slab allocator将其分配成很多slab trunk,每个trunk用来存储一类数据的,而真正存的数据很有可能不会被精确匹配,而我们需要给其找一个最佳的匹配机制就是用来slab trunk来存储 但是难免会浪费内存空间,但是可以让我们内存分配效率非常高

 

 

安装配置memcached服务

Memcached依赖于libeventAPI库,所以首先要安装libevent

[root@testtools]# tar xf libevent-2.0.21-stable.tar.gz 

[root@testtools]# cd libevent-2.0.21-stable

[root@testlibevent-2.0.21-stable]# ./configure --prefix=/usr/local/memcached--with-libevent=/usr/local/libevent

[root@testlibevent-2.0.21-stable]# make && make install

安装memcached

[root@testtools]#cd memcached-1.4.15

[root@testmemcached-1.4.15]# ./configure --prefix=/usr/local/memcached--with-libevent=/usr/local/libevent && make && make install

 

启动memcached

启动之前我们先来看一下memcahced关键的几个参数

关键参数说明

[root@node1bin]# ./memcached -h

-p 监听tcp协议的监听端口

-T 监听UDP协议id监听端口默认都是11211 

-s 如果只在本地通信那么可以将其监听在某个套接字上,比如mysql.scok 能够利用共享内存方式进行通信的

-c 最大并发连接数

-l 监听的地址,如果服务器有多块网卡,那么用-l来指定监听的某个网卡上

-d 以一个服务方式运行

-r 设定最大内核大小限制

-u 以某个用户身份运行

-m  以兆为单位指定memcached最大内存可用空间

-t 用于处理入站请求最大的线程数,仅在memcached编译时开启了支持线程才有效,而linux对线程支持是非常有限的,所以不用关心了

-f 设定slab定义预先分配内存空间大小固定的块时使用的增长因子

-n 最小的存储单位是多大,默认是48字节,单位是字节

-P 指定pid文件

-L 视图使用最多的内存空间

-S 启用SSL认证功能

启动memcached

这里先放在前台运行,并观察其增长因子,如下所示:

[root@testbin]# /usr/local/memcached/bin/memcached -m 128 -n 20 -f 1.25 -vv -u nobody

slabclass1: chunksize72 perslab14563

slabclass2: chunksize96 perslab 10922

slabclass3: chunksize120 perslab 8738

slabclass4: chunksize 152 perslab 6898

slabclass5: chunksize192 perslab5461

slabclass6: chunk size240 perslab4369

slabclass7: chunk size304 perslab3449

#################中间略#################

slabclass40: chunk size493552 perslab2

slabclass41: chunk size616944 perslab1

slabclass42: chunk size771184 perslab1

slabclass43: chunksize1048576perslab1

<26server listening (auto-negotiate)

<27server listening (auto-negotiate)

<28send buffer was 229376, now 268435456

<28server listening (udp)

<29send buffer was 229376, now 268435456

<29server listening (udp)

<28server listening (udp)

<29server listening (udp)

<28server listening (udp)

<29server listening (udp)

<28server listening (udp)

<29server listening (udp)

chunk size 表示最小为72 以1.25倍的方式增加

slab class 表示分配给Slab之后根据slab的大小切分成chunk,chunk用于缓存记录,特定大小的chunk组称为slab class

perslab 表示一共有多少个空闲空间可用

 

结束进程,将增长因子换种方式增长,并观察效果

[root@testbin]# /usr/local/memcached/bin/memcached -m 128 -n 20 -f 1.1 -vv -u nobody

slabclass1: chunksize72perslab14563

slabclass2:chunk size80 perslab13107

slabclass3:chunk size88 perslab11915

slabclass4: chunksize96perslab10922

slabclass5: chunk size112 perslab9362

slabclass6: chunk size128 perslab8192

###################中间略#################

slabclass95: chunk size750904 perslab1

slabclass96: chunk size826000 perslab1

slabclass97: chunk size908600 perslab1

slab class98chunk size1048576 perslab1

以上,我们很明确对比出其差别

 

由此接下来我们就将其放在后台运行,加入-d参数

[root@testbin]# /usr/local/memcached/bin/memcached -m 128 -n 20 -f 1.1-u nobody -d

查看监听端口

[root@testbin]#netstat -lntup | grep memcache

tcp00 0.0.0.0:112110.0.0.0:*LISTEN9464/memcached

tcp00 :::11211:::*LISTEN9464/memcached

udp00 0.0.0.0:112110.0.0.0:*9464/memcached

udp00 :::11211:::*9464/memcached

 

连接memcahced

[root@testbin]# telnet 127.0.0.1 11211

使用add命令为memcahced新建键

格式:

addkeyname flag  timeout  datasize

例如:

addmykey 0 10 12
Hello world!

<30add mykey 0 10 12
hello

<30add mykey 0 10 12
hello

 

让php支持memcached

[root@testtools]# tar xf memcache-2.2.6.tgz

[root@test tools]# cd memcache-2.2.6

[root@testmemcache-2.2.6]# /usr/local/php/bin/phpize

Configuringfor:

PHP ApiVersion:20100412

ZendModule Api No:20100525

ZendExtension Api No:220100525

[root@testmemcache-2.2.6]# ./configure --with-php-config=/usr/local/php/bin/php-config--enable-memcache

[root@testmemcache-2.2.6]# make && make install

编译成功后,可以看到有如下反馈信息,将其路径记下

Installingshared extensions:/usr/local/php/lib/php/extensions/no-debug-non-zts-20100525/

查看模块是否存在

[root@testmemcache-2.2.6]#ls/usr/local/php/lib/php/extensions/no-debug-non-zts-20100525/

memcache.so

将其模块写入至php.d/目录下

[root@testmemcache-2.2.6]# echo‘extension=/usr/local/php/lib/php/extensions/no-debug-non-zts-20100525/memcache.so‘>/etc/php.d/memcache.ini

重启fastcgi

[root@testmemcache-2.2.6]# /etc/init.d/php-fpm restart

访问phpinfo,并查看是否有memcache模块扩展,如下所示:

wKiom1NvQFbzGoI5AAGeblkTK6E176.jpg

 

对memcached功能进行测试

建立php测试页面,内容如下:

[root@node1htdocs]# vim test.php

 

<?php
$mem = new Memcache;

;创建memcache对象连接其11211端口 ,如果能连接则说明可以访问memcache
$mem->connect("127.0.0.1", 11211)  or die("Could notconnect"); 

 

;如果服务能连接上,则显示其版本号是多少
$version = $mem->getVersion();
echo "Server‘s version: ".$version."<br/>\n";

;并且创建key名称为hellokey 内容为hello world 期限为600秒,这里没有长度,其会自动判断长度
$mem->set(‘hellokey‘, ‘Hello World‘, 0, 600) or die("Failed to savedata at the memcached server");
echo "Store data in the cache (data will expire in 600seconds)<br/>\n";

$get_result = $mem->get(‘hellokey‘);
echo "$get_result is from memcached server.";    

?>

访问测试:

wKioL1NvQETAHriaAAC0cd5vZuc179.jpg

连接memcahced ,可以看到,我们保存的键值与上一致

[root@testhtdocs]#telnet 127.0.0.1 11211

Trying127.0.0.1...

Connectedto 127.0.0.1.

Escapecharacter is ‘^]‘.

get hellokey

VALUEhellokey 0 11

HelloWorld

END

由此可见,我们的php可以使用memcache了

 

将php的session会话信息保存至memcached当中

编辑php.ini配置文件

找到session相关参数

session.save_handler= files     #默认是使用文件保存session的

更改为:

session.save_handler= memcache

session.save_path="tcp://127.0.0.1:11211?persistent=1&weight=1&timeout=1&retry_interval=15"

如果memcahced使用的是公网服务器的话最好使用其公网地址

保存退出并重启服务

[root@testhtdocs]# /etc/init.d/php-fpm restart

Gracefullyshutting down php-fpm . done

Startingphp-fpmdone

创建测试页setsession.php

[root@testhtdocs]# cat setsession.php

<?php

session_start();

if(!isset($_SESSION[‘test‘])) {

$_SESSION[‘test‘] = time();

}

print$_SESSION[‘test‘];

print"<br><br>";

print"Session ID: " . session_id();

?>

 

新建php页面showsess.php,获取当前用户的会话ID:

[root@testhtdocs]# cat showsess.php

<?php

session_start();

$memcache_obj= new Memcache;

$memcache_obj->connect(‘127.0.0.1‘,11211);

$mysess=session_id();

var_dump($memcache_obj->get($mysess));

$memcache_obj->close();

?>

而后找一其他主机,使用负载均衡轮询到不同的主机上,可以发现无论刷新至哪个页面 其用户的session是一样的

访问setsession.php

wKioL1NvQGGDh61cAACaddedWVg504.jpg

1399775256         #为获取时间
Session ID: 9a0itlgjlurghq83ibvmol5pc7     #为session的id号

 

获取session

wKiom1NvQKvTZZ99AACHnmI4r1g629.jpg

可以看到其时间是与上面setsession是一样的

 

总结

这样一来比php将session保存在本地效率要快很多,如果以后要使用多台memcached的话至于开发程序能否实现负载均衡,到底是使用取余的算法还是使用一致性哈希的算法完全要看开发人员的决定了

感谢各位。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

本文出自 “心情依旧” 博客,请务必保留此出处http://yijiu.blog.51cto.com/433846/1409459