扫盲:php session缓存至memcached中的方法
memcached是一套分布式的快取系統(tǒng),當(dāng)初是Danga?Interactive為了LiveJournal所發(fā)展的,但被許多軟件(如MediaWiki)所使用。這是一套開放源代碼軟件,以BSD?license授權(quán)協(xié)議發(fā)布。[1]
memcached僅支持一些非常簡單的命令?比如get(獲取某個鍵值)?set(用來設(shè)定或保存一個緩存);
其本身是緩存服務(wù)器,但本身無法決定緩存任何數(shù)據(jù),其緩存機(jī)制依賴于服務(wù)端和客戶端兩者必不可少(存儲是由服務(wù)端進(jìn)行存儲,但存儲什么是由客戶端進(jìn)行決定的)
因此客戶端要自己提供緩存的鍵名以及時長、標(biāo)志位、整個數(shù)據(jù)大小等等
?
例如:只存儲hello?但只存儲60秒
set?key?5?60?hello
并告知服務(wù)器端,這樣存儲過了60秒后,由服務(wù)端進(jìn)行清除數(shù)據(jù)
但是其工作機(jī)制非常獨(dú)特,其緩存功能是基于Lazy模型的:
只要空間未滿則不清理
?
那么問題來了:如果空間過小,而需緩存的內(nèi)容過大的話,那么導(dǎo)致緩存抖動非常嚴(yán)重,存完即清理其次再去緩存這樣會導(dǎo)致命中率下降,而毫無意義
有些時候,有些數(shù)據(jù)管理不善有可能導(dǎo)致緩存崩潰等
如果memcached崩潰僅導(dǎo)致業(yè)務(wù)層的影響,最多是速度降低?而不會導(dǎo)致數(shù)據(jù)層
?
memcached如何實(shí)現(xiàn)緩存的
memcached通過內(nèi)存進(jìn)行緩存數(shù)據(jù),但并不實(shí)現(xiàn)持久緩存
?
存數(shù)數(shù)據(jù)的下限:
最小為48字節(jié)
最大不能超過1MB
?
但存儲的數(shù)據(jù)大小有可能不一致,比如:
index.html10k
test.jpg34k
?
那memcached如何在內(nèi)存中管理緩存數(shù)據(jù)
?
假如我們分別存儲不同大小的數(shù)據(jù)以上為例
很顯然只要分配一個足夠大的空間就可以了,但是在內(nèi)存中去找對應(yīng)的數(shù)據(jù)我們必須要有對應(yīng)的緩存對象的邊界(起始存儲位地址和結(jié)束存儲位地址)將其當(dāng)做獨(dú)立的單位來管理
等其緩存失效了,空間會被騰出,時間久了可能會帶來碎片,因?yàn)榇鎯Φ亩际欠浅P〉臄?shù)據(jù)單元,按理說如果再想高速利用則會困難,所以在這種機(jī)制下memcached的存儲數(shù)據(jù)?查詢數(shù)據(jù)等操作都是非常緩慢的
由此,不停快速基于內(nèi)存的申請、釋放反復(fù)操作,這種釋放本身也消耗大量的資源和時間
因此我們需要一種高效的機(jī)制來解決內(nèi)存的創(chuàng)建和釋放的問題
對于memcached來講首要必須解決這類內(nèi)存碎片問題,不然由于內(nèi)存的碎片導(dǎo)致進(jìn)程運(yùn)行的非常緩慢
?
在linux內(nèi)核中引入了兩種機(jī)制避免內(nèi)存碎片
?
1.buddy?system????伙伴系統(tǒng)
為了實(shí)現(xiàn)整個內(nèi)存中以頁面方式管理內(nèi)存的時候有足夠大的連續(xù)內(nèi)存空間可用的,在物理內(nèi)存中,事實(shí)上內(nèi)存的管理和分配在內(nèi)核級別通常以頁面方式分配和使用的
通常是4k大小一個頁面,buddy就是為了將這些零碎的、空閑的合并成一個連續(xù)的大的內(nèi)存空間,這樣就避免了頁面之間產(chǎn)生碎片的,因此,其主要目的是為了避免內(nèi)存外碎片
?
2.slab?allocator??slab分配器
實(shí)現(xiàn)將存儲小于頁面單位的非常小的數(shù)據(jù)內(nèi)存結(jié)構(gòu)的時候之前事先分配并隨時等待有需求的進(jìn)程或要存儲的對象使用,當(dāng)我們使用之后它也不會自動消毀結(jié)構(gòu)而是隨時重復(fù)使用
避免內(nèi)存內(nèi)部碎片
?
最新版本的memcached使用的是增長因子(growth?factor)來明確定義起始點(diǎn)開始依次增長
比如:
我們定義增長因子為其2倍
我們存儲一個單位為48bytes,那么會分配其48*2?=?96bytes
如果增長因子為1.1倍
那么48+48*1.1
一旦存儲空間滿了,則會清理,沒有存滿則不會清理數(shù)據(jù)
?
memcached也支持分布式緩存基礎(chǔ)概念
memcached也支持分布式緩存,但是彼此之間不會通信,但是一個memcached也不會緩存過多數(shù)據(jù),如果將來緩存的數(shù)據(jù)很大的話,那只能使用多臺memcached提供服務(wù)
假設(shè)這么一個場景:
前端的應(yīng)用服務(wù)器很多,這些服務(wù)器角色都需要連接至數(shù)據(jù)庫,為了加速數(shù)據(jù)庫的性能,可對其提供memcached服務(wù)器,經(jīng)過一段時間觀察,我們的memcached數(shù)據(jù)庫緩存過多,請求量過大一臺機(jī)器根本扛不住,這時需要對其擴(kuò)容。
于是我們又加入了一臺memcached,但是memcached服務(wù)器之間是不能通信的
所以某一應(yīng)用服務(wù)器A?要緩存數(shù)據(jù)不能只盯一臺memcached,所以我們要讓其輪流來提供服務(wù),但是對memcached而言它的分布式算法是在應(yīng)用程序(客戶端)中實(shí)現(xiàn)的,而不是取決于服務(wù)器自身
?
客戶端是如何分布式調(diào)度服務(wù)端
需要一定分布式算法,和調(diào)度器
(如果memcached非常多的話,完全可以使用第三方調(diào)度器進(jìn)行調(diào)度,比如nginx+持久連接)
?
或一致性哈希算法:
參考:http://blog.csdn.net/kongqz/article/details/6695417
?
總結(jié):
1、在內(nèi)存中緩存
2、內(nèi)存要使用slab?allocator將其分配成很多slab?trunk,每個trunk用來存儲一類數(shù)據(jù)的,而真正存的數(shù)據(jù)很有可能不會被精確匹配,而我們需要給其找一個最佳的匹配機(jī)制就是用來slab?trunk來存儲?但是難免會浪費(fèi)內(nèi)存空間,但是可以讓我們內(nèi)存分配效率非常高
?
?
安裝配置memcached服務(wù)
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關(guān)鍵的幾個參數(shù)
關(guān)鍵參數(shù)說明
[root@node1bin]#?./memcached?-h
-p?監(jiān)聽tcp協(xié)議的監(jiān)聽端口
-T?監(jiān)聽UDP協(xié)議id監(jiān)聽端口默認(rèn)都是11211?
-s?如果只在本地通信那么可以將其監(jiān)聽在某個套接字上,比如mysql.scok?能夠利用共享內(nèi)存方式進(jìn)行通信的
-c?最大并發(fā)連接數(shù)
-l?監(jiān)聽的地址,如果服務(wù)器有多塊網(wǎng)卡,那么用-l來指定監(jiān)聽的某個網(wǎng)卡上
-d?以一個服務(wù)方式運(yùn)行
-r?設(shè)定最大內(nèi)核大小限制
-u?以某個用戶身份運(yùn)行
-m??以兆為單位指定memcached最大內(nèi)存可用空間
-t?用于處理入站請求最大的線程數(shù),僅在memcached編譯時開啟了支持線程才有效,而linux對線程支持是非常有限的,所以不用關(guān)心了
-f?設(shè)定slab定義預(yù)先分配內(nèi)存空間大小固定的塊時使用的增長因子
-n?最小的存儲單位是多大,默認(rèn)是48字節(jié),單位是字節(jié)
-P?指定pid文件
-L?視圖使用最多的內(nèi)存空間
-S?啟用SSL認(rèn)證功能
啟動memcached
這里先放在前臺運(yùn)行,并觀察其增長因子,如下所示:
[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之后根據(jù)slab的大小切分成chunk,chunk用于緩存記錄,特定大小的chunk組稱為slab?class
perslab?表示一共有多少個空閑空間可用
?
結(jié)束進(jìn)程,將增長因子換種方式增長,并觀察效果
[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?class98:?chunk?size1048576?perslab1
以上,我們很明確對比出其差別
?
由此接下來我們就將其放在后臺運(yùn)行,加入-d參數(shù)
[root@testbin]#?/usr/local/memcached/bin/memcached?-m?128?-n?20?-f?1.1-u?nobody?-d
查看監(jiān)聽端口
[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模塊擴(kuò)展,如下所示:
?
對memcached功能進(jìn)行測試
建立php測試頁面,內(nèi)容如下:
[root@node1htdocs]#?vim?test.php
?
<?php
$mem?=?new?Memcache;
;創(chuàng)建memcache對象連接其11211端口?,如果能連接則說明可以訪問memcache
$mem->connect("127.0.0.1",?11211)??or?die("Could?notconnect");?
?
;如果服務(wù)能連接上,則顯示其版本號是多少
$version?=?$mem->getVersion();
echo?"Server's?version:?".$version."<br/>\n";
;并且創(chuàng)建key名稱為hellokey?內(nèi)容為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.";????
?>
訪問測試:
連接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當(dāng)中
編輯php.ini配置文件
找到session相關(guān)參數(shù)
session.save_handler=?files?????#默認(rèn)是使用文件保存session的
更改為:
session.save_handler=?memcache
session.save_path="tcp://127.0.0.1:11211?persistent=1&weight=1&timeout=1&retry_interval=15"
如果memcahced使用的是公網(wǎng)服務(wù)器的話最好使用其公網(wǎng)地址
保存退出并重啟服務(wù)
[root@testhtdocs]#?/etc/init.d/php-fpm?restart
Gracefullyshutting?down?php-fpm?.?done
Startingphp-fpmdone
創(chuàng)建測試頁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,獲取當(dāng)前用戶的會話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();
?>
而后找一其他主機(jī),使用負(fù)載均衡輪詢到不同的主機(jī)上,可以發(fā)現(xiàn)無論刷新至哪個頁面?其用戶的session是一樣的
訪問setsession.php
1399775256?????????#為獲取時間
Session?ID:?9a0itlgjlurghq83ibvmol5pc7?????#為session的id號
?
獲取session
可以看到其時間是與上面setsession是一樣的
?
總結(jié)
這樣一來比php將session保存在本地效率要快很多,如果以后要使用多臺memcached的話至于開發(fā)程序能否實(shí)現(xiàn)負(fù)載均衡,到底是使用取余的算法還是使用一致性哈希的算法完全要看開發(fā)人員的決定了
感謝各位。
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
總結(jié)
以上是生活随笔為你收集整理的扫盲:php session缓存至memcached中的方法的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 面向对象编程从骨子里就有问题——看看名人
- 下一篇: 构建SAN网络存储