memcache相關信息

MemCache

memcache是一套分佈式的高速緩存系統,由LiveJournal的Brad Fitzpatrick開發,但目前被許多網站使用以提升網站的訪問速度,尤其對於一些大型的、需要頻繁訪問數據庫的網站訪問速度提升效果十分顯著。這是一套開放源代碼軟件,以BSD license授權發佈。


Memcache應用場景介紹

面臨的問題

對於高併發高訪問的Web應用程序來說,數據庫存取瓶頸一直是個令人頭疼的問題。特別當你的程序架構還是建立在單數據庫模式,而一個數據池連接數峯 值已經達到500的時候,那你的程序運行離崩潰的邊緣也不遠了。很多小網站的開發人員一開始都將注意力放在了產品需求設計上,缺忽視了程序整體性能,可擴 展性等方面的考慮,結果眼看着訪問量一天天網上爬,可突然發現有一天網站因爲訪問量過大而崩潰了,到時候哭都來不及。所以我們一定要未雨綢繆,在數據庫還 沒罷工前,想方設法給它減負,這也是這篇文章的主要議題。

大家都知道,當有一個request過來後,web服務器交給app服務器,app處理並從db中存取相關數據,但db存取的花費是相當高昂的。特 別是每次都取相同的數據,等於是讓數據庫每次都在做高耗費的無用功,數據庫如果會說話,肯定會發牢騷,你都問了這麼多遍了,難道還記不住嗎?是啊,如果 app拿到第一次數據並存到內存裏,下次讀取時直接從內存裏讀取,而不用麻煩數據庫,這樣不就給數據庫減負了?而且從內存取數據必然要比從數據庫媒介取快 很多倍,反而提升了應用程序的性能。

因此,我們可以在web/app層與db層之間加一層cache層,主要目的:1. 減少數據庫讀取負擔;2. 提高數據讀取速度。而且,cache存取的媒介是內存,而一臺服務器的內存容量一般都是有限制的,不像硬盤容量可以做到TB級別。所以,可以考慮採用分佈 式的cache層,這樣更易於破除內存容量的限制,同時又增加了靈活性。

Memcached 介紹

Memcached是開源的分佈式cache系統,現在很多的大型web應用程序包括 facebook,youtube,wikipedia,yahoo等等都在使用memcached來支持他們每天數億級的頁面訪問。通過把cache層 與他們的web架構集成,他們的應用程序在提高了性能的同時,還大大降低了數據庫的負載。 具體的memcached資料大家可以直接從它的官方網站[1]上得到。這裏我就簡單給大家介紹一下memcached的工作原理:

Memcached處理的原子是每一個(key,value)對(以下簡稱kv對),key會通過一個hash算法轉化成hash-key,便於查找、對比以及做到儘可能的散列。同時,memcached用的是一個二級散列,通過一張大hash表來維護。

Memcached有兩個核心組件組成:服務端(ms)和客戶端(mc),在一個memcached的查詢中,mc先通過計算key的hash值來 確定kv對所處在的ms位置。當ms確定後,客戶端就會發送一個查詢請求給對應的ms,讓它來查找確切的數據。因爲這之間沒有交互以及多播協議,所以 memcached交互帶給網絡的影響是最小化的。

舉例說明:考慮以下這個場景,有三個mc分別是X,Y,Z,還有三個ms分別是A,B,C:

設置kv對 X想設置key=”foo”,value=”seattle” X拿到ms列表,並對key做hash轉化,根據hash值確定kv對所存的ms位置 B被選中了 X連接上B,B收到請求,把(key=”foo”,value=”seattle”)存了起來

獲取kv對 Z想得到key=”foo”的value Z用相同的hash算法算出hash值,並確定key=”foo”的值存在B上 Z連接上B,並從B那邊得到value=”seattle” 其他任何從X,Y,Z的想得到key=”foo”的值的請求都會發向B

Memcached服務器(ms)

內存分配

默認情況下,ms是用一個內置的叫“塊分配器”的組件來分配內存的。捨棄c++標準的malloc/free的內存分配,而採用塊分配器的主要目的 是爲了避免內存碎片,否則操作系統要花費更多時間來查找這些邏輯上連續的內存塊(實際上是斷開的)。用了塊分配器,ms會輪流的對內存進行大塊的分配,並 不斷重用。當然由於塊的大小各不相同,當數據大小和塊大小不太相符的情況下,還是有可能導致內存的浪費。

同時,ms對key和data都有相應的限制,key的長度不能超過250字節,data也不能超過塊大小的限制 --- 1MB。 因爲mc所使用的hash算法,並不會考慮到每個ms的內存大小。理論上mc會分配概率上等量的kv對給每個ms,這樣如果每個ms的內存都不太一樣,那 可能會導致內存使用率的降低。所以一種替代的解決方案是,根據每個ms的內存大小,找出他們的最大公約數,然後在每個ms上開n個容量=最大公約數的 instance,這樣就等於擁有了多個容量大小一樣的子ms,從而提供整體的內存使用率。

緩存策略

當ms的hash表滿了之後,新的插入數據會替代老的數據,更新的策略是LRU(最近最少使用),以及每個kv對的有效時限。Kv對存儲有效時限是在mc端由app設置並作爲參數傳給ms的。

同時ms採用是偷懶替代法,ms不會開額外的進程來實時監測過時的kv對並刪除,而是當且僅當,新來一個插入的數據,而此時又沒有多餘的空間放了,纔會進行清除動作。

緩存數據庫查詢 現在memcached最流行的一種使用方式是緩存數據庫查詢,下面舉一個簡單例子說明:

App需要得到userid=xxx的用戶信息,對應的查詢語句類似:

“SELECT * FROM users WHERE userid = xxx”

App先去問cache,有沒有“user:userid”(key定義可預先定義約束好)的數據,如果有,返回數據;如果沒有,App會從數據庫中讀取數據,並調用cache的add函數,把數據加入cache中。

當取的數據需要更新,app會調用cache的update函數,來保持數據庫與cache的數據同步。

從上面的例子我們也可以發現,一旦數據庫的數據發現變化,我們一定要及時更新cache中的數據,來保證app讀到的是同步的正確數據。當然我們可 以通過定時器方式記錄下cache中數據的失效時間,時間一過就會激發事件對cache進行更新,但這之間總會有時間上的延遲,導致app可能從 cache讀到髒數據,這也被稱爲狗洞問題。(以後我會專門描述研究這個問題)

數據冗餘與故障預防

從設計角度上,memcached是沒有數據冗餘環節的,它本身就是一個大規模的高性能cache層,加入數據冗餘所能帶來的只有設計的複雜性和提高系統的開支。

當一個ms上丟失了數據之後,app還是可以從數據庫中取得數據。不過更謹慎的做法是在某些ms不能正常工作時,提供額外的ms來支持cache,這樣就不會因爲app從cache中取不到數據而一下子給數據庫帶來過大的負載。

同時爲了減少某臺ms故障所帶來的影響,可以使用“熱備份”方案,就是用一臺新的ms來取代有問題的ms,當然新的ms還是要用原來ms的IP地址,大不了數據重新裝載一遍。

另外一種方式,就是提高你ms的節點數,然後mc會實時偵查每個節點的狀態,如果發現某個節點長時間沒有響應,就會從mc的可用server列表裏 刪除,並對server節點進行重新hash定位。當然這樣也會造成的問題是,原本key存儲在B上,變成存儲在C上了。所以此方案本身也有其弱點,最好 能和“熱備份”方案結合使用,就可以使故障造成的影響最小化。

Memcached客戶端(mc)

Memcached客戶端有各種語言的版本供大家使用,包括java,c,php,.net等等,具體可參見memcached api page[2]。 大家可以根據自己項目的需要,選擇合適的客戶端來集成。

緩存式的Web應用程序架構 有了緩存的支持,我們可以在傳統的app層和db層之間加入cache層,每個app服務器都可以綁定一個mc,每次數據的讀取都可以從ms中取得,如果 沒有,再從db層讀取。而當數據要進行更新時,除了要發送update的sql給db層,同時也要將更新的數據發給mc,讓mc去更新ms中的數據。

假設今後我們的數據庫可以和ms進行通訊了,那可以將更新的任務統一交給db層,每次數據庫更新數據的同時會自動去更新ms中的數據,這樣就可以進一步減少app層的邏輯複雜度。如下圖:

不過每次我們如果沒有從cache讀到數據,都不得不麻煩數據庫。爲了最小化數據庫的負載壓力,我們可以部署數據庫複寫,用slave數據庫來完成讀取操作,而master數據庫永遠只負責三件事:1.更新數據;2.同步slave數據庫;3.更新cache。如下圖:

以上這些緩存式web架構在實際應用中被證明是能有效並能極大地降低數據庫的負載同時又能提高web的運行性能。當然這些架構還可以根據具體的應用環境進行變種,以達到不同硬件條件下性能的最優化。


Memcache的部署和使用

一、memcache簡介

Memcachedanga.com的一個項目,最早是爲 LiveJournal 服務的,目前全世界不少人使用這個緩存項目來構建自己大負載的網站,來分擔數據庫的壓力。

Memcache官方網站:http://memcached.org/

 

二、memcache的安裝

1、  下載源文件(目前最新穩定版本是memcached-1.4.5.tar.gz)

wget http://memcached.googlecode.com/files/memcached-1.4.5.tar.gz

由於Memcache用到了libevent這個庫用於Socket的處理,所以還需要安裝libevent,

Libevent的官網爲http://www.monkey.org/~provos/libevent/,最新穩定版本爲:1.4.14b

wget http://www.monkey.org/~provos/libevent-1.4.14b-stable.tar.gz

2、  安裝memcache

1)、安裝libevent

# tar xzvf libevent-1.4.14b-stable.tar.gz

# cd libevent-1.4.14b-stable

# ./configure --prefix=/usr

# make && make install

# cd ..

# ls -al /usr/lib | grep libevent (查看是否安裝成功)

2)、安裝memcache

# tar xzvf memcached-1.4.5.tar.gz

# cd memcached-1.4.5

# ./configure --with-libevent=/usr

# make && make install

# ls -al /usr/local/bin/mem*   (查看是否生成memcached可執行程序文件)

#備註:如果是64位的系統可能會報錯,因爲默認的庫文件只會安裝到/usr/lib下面

3、  啓動memcache

# 通過/usr/local/bin/memcached –h查看命令選項幫助

[root@Centos2 memcached-1.4.5]# /usr/local/bin/memcached -h

memcached 1.4.5

-p       TCP port number to listen on (default: 11211)

-U       UDP port number to listen on (default: 11211, 0 is off)

-s      UNIX socket path to listen on (disables network support)

-a      access mask for UNIX socket, in octal (default: 0700)

-l   interface to listen on (default: INADDR_ANY, all addresses)

-d            run as a daemon

-r            maximize core file limit

-u assume identity of (only when run as root)

-m       max memory to use for items in megabytes (default: 64 MB)

-M            return error on memory exhausted (rather than removing items)

-c       max simultaneous connections (default: 1024)

#啓動命令如下

# /usr/local/bin/memcached -d -m 10  -u root -l 172.28.5.2 -p 12000 -c 1024 –P /tmp/memcached.pid

####相關選項說明

-d   表示啓動一個守護進程

-m 是分配給memcached使用的內存

-u   運行memcached的用戶

-l     是memcached監聽的ip

-p    是memcached監聽的端口

-c    memcache運行的最大併發連接數

-P    是設置memcache的pid文件

 

三、Php的memcache擴展的安裝

1、  windows平臺memcache擴展的安裝

下載相應版本的php_memcache.dll文件,然後將該文件放到D:\AppServ\php5\ext目錄下(路徑根據php安裝的位置不同請自行設置),然後更改相應的php.ini的設置,創建的php文件,訪問這個文件看memcache模塊是否起作用。

2、  linux平臺memcache擴展的安裝

wget http://blog.s135.com/soft/linux/nginx_php/memcache/memcache-2.2.5.tgz

tar zxvf memcache-2.2.5.tgz
cd memcache-2.2.5/
/usr/local/php/bin/phpize
./configure --with-php-config=/usr/local/php/bin/php-config
make
make install
cd ../

修改php.ini文件

查找/usr/local/php/etc/php.ini中的 extension_dir = "./"修改爲extension_dir = "/usr/local/php/lib/php/extensions/no-debug-non-zts-20060613/"

並在此行後增加一行,然後保存:

extension = "memcache.so"

創建php文件aaa.php,內容如下:

Phpinfo();

?>

保存,然後訪問看是否能顯示memcache模塊的信息!

 

四、Memcache的使用

//使用部分是轉載的,原文地址:http://blog.csdn.net/heiyeshuwu

接口介紹 ]
服務器端和客戶端都安裝配置好了,現在我們就來測試以下我們的成果。Memcache客戶端包含兩組接口,一組是面向過程的接口,一組是面向對象的接口,具體可以參考PHP手冊 “LXXV. Memcache Functions” 這章。我們爲了簡單方便,就使用面向對象的方式,也便於維護和編寫代碼。Memcache面向對象的常用接口包括:

Memcache::connect -- 打開一個到Memcache的連接
Memcache::pconnect -- 
打開一個到Memcache的長連接
Memcache::close -- 
關閉一個Memcache的連接
Memcache::set -- 
保存數據到Memcache服務器上
Memcache::get -- 
提取一個保存在Memcache服務器上的數據
Memcache::replace -- 
替換一個已經存在Memcache服務器上的項目(功能類似Memcache::set
Memcache::delete -- 
Memcache服務器上刪除一個保存的項目
Memcache::flush -- 
刷新所有Memcache服務器上保存的項目(類似於刪除所有的保存的項目)
Memcache::getStats -- 
獲取當前Memcache服務器運行的狀態

測試代碼 ]
現在我們開始一段測試代碼:

//連接
$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();

//添加多臺memcached服務器

$b = new Memcache(); 
$b->addServer("10.55.38.18",11271); 
$b->addServer("10.55.38.18",11272); 
$b->addServer("10.55.38.18",11273);

 

 

$arrHosts = array('10.10.10.11'=>'11211','10.10.10.12'=>'11211'); 

$mc = new Memcache(); 
echo 
serialize($mc)."\n"
foreach(
$arrHosts as $host=>$port) { 
echo 
'start '.$host."\n"
$mc->connect($host,$port);

echo serialize($mc)."\n"
$stats $mc->getStats(); 
echo 
$stats['pid']."\n";

}


?>


如果正常的話,瀏覽器將輸出:
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安裝成功,我們再來分析以下上面的這段程序。


程序分析 ]

初始化一個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'060);

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'060);
$val = $mem->get('key1');
echo "
Get key1 value: " . $val;

同樣的,Memcache也是可以保存數組的,下面是在Memcache上面保存了一個數組,然後獲取回來並輸出
$arr = array('aaa', 'bbb', 'ccc''ddd');
$mem->set('key2'$arr060);
$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 
"
";


發佈了132 篇原創文章 · 獲贊 22 · 訪問量 20萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章