memcached+php環境配置和分析

y0umer發表於2011-07-29

 版本:memcached-1.2.1-win32.zip

簡介:

一、memcached 簡介

在很多場合,我們都會聽到 memcached 這個名字,但很多同學只是聽過,並沒有用過或實際瞭解過,只知道它是一個很不錯的東東。這裡簡單介紹一下,memcached 是高效、快速的分散式記憶體物件快取系統,主要用於加速 WEB 動態應用程式。

工作原理:

memcached 的工作原理

首先 memcached 是以守護程式方式執行於一個或多個伺服器中,隨時接受客戶端的連線操作,客戶端可以由各種語言編寫,目前已知的客戶端 API 包括 Perl/PHP/Python/Ruby/Java/C#/C 等等。PHP 等客戶端在與 memcached 服務建立連線之後,接下來的事情就是存取物件了,每個被存取的物件都有一個唯一的識別符號 key,存取操作均通過這個 key 進行,儲存到 memcached 中的物件實際上是放置記憶體中的,並不是儲存在 cache 檔案中的,這也是為什麼 memcached 能夠如此高效快速的原因。注意,這些物件並不是持久的,服務停止之後,裡邊的資料就會丟失。

PHP 如何作為 memcached 客戶端
有兩種方法可以使 PHP 作為 memcached 客戶端,呼叫 memcached 的服務進行物件存取操作。 第一種,PHP 有一個叫做 memcache 的擴充套件,Linux 下編譯時需要帶上 –enable-memcache[=DIR] 選項,Window 下則在 php.ini 中去掉 php_memcache.dll 前邊的註釋符,使其可用。 除此之外,還有一種方法,可以避開擴充套件、重新編譯所帶來的麻煩,那就是直接使用 php-memcached-client。 本文選用第二種方式,雖然效率會比擴充套件庫稍差一些,但問題不大。

四、PHP memcached 應用示例
首先 下載 memcached-client.php,在下載了 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` 的物件資料進行存取操作:

<?php
//   包含 memcached 類檔案
require_once(`memcached-client.php`);
//   選項設定
$options = array(
     `servers` => array(`192.168.1.1:11211`), //memcached 服務的地址、埠,可用多個陣列元素表示多個 memcached 服務
     `debug` => true,   //是否開啟 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 “n”.str_pad(`$mc->add() `, 60, `_`).”n”;
var_dump($val);
//   替換已寫入的物件資料值
$mc->replace($key, array(`some`=>`haha`, `array`=>`xxx`));
$val = $mc->get($key);
echo “n”.str_pad(`$mc->replace() `, 60, `_`).”n”;
var_dump($val);
//   刪除 memcached 中的物件
$mc->delete($key);
$val = $mc->get($key);
echo “n”.str_pad(`$mc->delete() `, 60, `_`).”n”;
var_dump($val);
?>
是不是很簡單,在實際應用中,通常會把資料庫查詢的結果集儲存到 memcached 中,下次訪問時直接從 memcached 中獲取,而不再做資料庫查詢操作,這樣可以在很大程度上減輕資料庫的負擔。通常會將 SQL 語句 md5() 之後的值作為唯一識別符號 key。下邊是一個利用 memcached 來快取資料庫查詢結果集的示例(此程式碼片段緊接上邊的示例程式碼):
<?php
$sql = `SELECT * FROM users`;
$key = md5($sql);   //memcached 物件識別符號
{
     //   在 memcached 中未獲取到快取資料,則使用資料庫查詢獲取記錄集。
     echo “n”.str_pad(`Read datas from MySQL.`, 60, `_`).”n”;
     $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);
{
     echo “n”.str_pad(`Read datas from memcached.`, 60, `_`).”n”;
}
var_dump($datas);
?>

可以看出,使用 memcached 之後,可以減少資料庫連線、查詢操作,資料庫負載下來了,指令碼的執行速度也提高了。之前我曾經寫過一篇名為《PHP 實現多伺服器共享 SESSION 資料》文章,文中的 SESSION 是使用資料庫儲存的,在併發訪問量大的時候,伺服器的負載會很大,經常會超出 MySQL 最大連線數,利用 memcached,我們可以很好地解決這個問題,工作原理如下:

  • 使用者訪問網頁時,檢視 memcached 中是否有當前使用者的 SESSION 資料,使用 session_id() 作為唯一識別符號;如果資料存在,則直接返回,如果不存在,再進行資料庫連線,獲取 SESSION 資料,並將此資料儲存到 memcached 中,供下次使用;
  • 當前的 PHP 執行結束(或使用了 session_write_close())時,會呼叫 My_Sess::write() 方法,將資料寫入資料庫,這樣的話,每次仍然會有資料庫操作,對於這個方法,也需要進行優化。使用一個全域性變數,記錄使用者進入頁面時的 SESSION 資料,然後在 write() 方法內比較此資料與想要寫入的 SESSION 資料是否相同,不同才進行資料庫連線、寫入資料庫,同時將 memcached 中對應的物件刪除,如果相同的話,則表示 SESSION 資料未改變,那麼就可以不做任何操作,直接返回了;
  • 那麼使用者 SESSION 過期時間怎麼解決呢?記得 memcached 的 add() 方法有個過期時間引數 $exp 嗎?把這個引數值設定成小於 SESSION 最大存活時間即可。另外別忘了給那些一直線上的使用者延續 SESSION 時長,這個可以在 write() 方法中解決,通過判斷時間,符合條件則更新資料庫資料。 

memcache安裝及測試 on windows
下載memcached for Win32地址:http://jehiah.cz/projects/memcached-win32/
該頁面不僅能下載,還有簡單而詳細的安裝步驟,翻譯過來是。
伺服器端安裝:
    1、解壓二進位制包到你希望的目錄(如:c:memcached)
     2、用Win的CMD安裝服務,在命令列下輸入 c:/memcached/memcached.exe -d install
     3、命令列下啟動memcached 服務 c:/memcached/memcached.exe -d start
     4、使用預設的11211埠監聽。
客戶端安裝:
執行完上述步驟後就該為php加上memcache擴充套件了。
   1、從pecl-5.2.3-Win32.zip中把php_memcache.dll拷貝到php安裝目錄的ext目錄下。
   2、將在php.ini中加入擴充套件 extension=php_memcache.dll
   3、重啟Apache伺服器
   4、測試memcache
在網站目錄下建立一個memcache.php,程式碼如下:
<?php
$memcache = memcache_connect(`localhost`, 11211);
if ($memcache) {
$memcache->set(“str_key”, “String to store in memcached”);
$memcache->set(“num_key”, 123);
$object = new StdClass;
$object->attribute = `test`;
$memcache->set(“obj_key”, $object);
$array = Array(`assoc`=>123, 345, 567);
$memcache->set(“arr_key”, $array);
var_dump($memcache->get(`str_key`));
var_dump($memcache->get(`num_key`));
var_dump($memcache->get(`obj_key`));
}
else {
echo “Connection to memcached failed”;
}
?>
如果正常輸出以下內容,則安裝就成功了
string(28) “String to store in memcached” string(3) “123” object(stdClass)#3 (1) { [“attribute”]=> string(4) “test” }
否則請檢查括展是否設定,ext目錄下是否有php_memcache.dll,記得一定要重啟apache。
執行memcache.exe -h 可檢視幫助,其中一些常用命令如下:

安裝: x:/memcached/memcached.exe -d install
啟動: x:/memcached/memcached.exe -d start
停止: x:/memcached/memcached.exe -d stop
重啟: x:/memcached/memcached.exe -d restart
幫助: x:/memcached/memcached.exe -h

memcached的基本設定
-p 監聽的埠
-l 連線的IP地址, 預設是本機
-d start 啟動memcached服務
-d restart 重起memcached服務
-d stop|shutdown 關閉正在執行的memcached服務
-d install 安裝memcached服務
-d uninstall 解除安裝memcached服務
-u 以的身份執行 (僅在以root執行的時候有效)
-m 最大記憶體使用,單位MB。預設64MB
-M 記憶體耗盡時返回錯誤,而不是刪除項
-c 最大同時連線數,預設是1024
-f 塊大小增長因子,預設是1.25
-n 最小分配空間,key+value+flags預設是48
-h 顯示幫助

Memcache 方法列表:

客戶端在與 memcached 服務建立連線之後,進行存取物件的操作,每個被存取的物件都有一個唯一的識別符號 key,存取操作均通過這個 key 進行,儲存到 memcached 中的物件實際上是放置記憶體中的,並不是儲存在 cache 檔案中的,這也是為什麼 memcached 能夠如此高效快速的原因。注意,這些物件並不是持久的,服務停止之後,裡邊的資料就會丟失
使用篇
   情況1:
   用add方法捕獲從資料庫中返回的一千行記錄失敗。當limit 限制從資料庫中返回的數量時又能成果功。
   檢視Memchache的文件後發現memcache對key和value的大小都有限制。
   key :250個字元以內。   value:大小在1M以內。

文件原文:http://www.socialtext.net/memcached/index.cgi?faq#how_do_i_access_memcached

參考資料:
對Memcached有疑問的朋友可以參考下列文章:
Linux下的Memcache安裝:http://www.ccvita.com/257.html
Windows下的Memcache安裝:http://www.ccvita.com/258.html
Memcache基礎教程:http://www.ccvita.com/259.html
Discuz!的Memcache快取實現:http://www.ccvita.com/261.html
Memcache協議中文版:http://www.ccvita.com/306.html
===================================================
例子:

< ?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 .”<br />”;
//替換資料
$mem->replace(`key1`, `This is replace value`, 0, 60);
$val = $mem->get(`key1`);
echo “Get key1 value: ” . $val . “<br />”;
//儲存陣列
$arr = array(`aaa`, `bbb`, `ccc`, `ddd`);
$mem->set(`key2`, $arr, 0, 60);
$val2 = $mem->get(`key2`);
echo “Get key2 value: “;
print_r($val2);
echo “<br />”;
//刪除資料
$mem->delete(`key1`);
$val = $mem->get(`key1`);
echo “Get key1 value: ” . $val . “<br />”;
//清除所有資料
$mem->flush();
$val2 = $mem->get(`key2`);
echo “Get key2 value: “;
print_r($val2);
echo “<br />”;
//關閉連線
$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的使用
使用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伺服器的訪問,能夠有效的阻止一些非法訪問,相應的也可以增加一些其他的規則來加強安全性,這個可以根據自己的需要來做。

本文章是整理收集而來,主要來源是:http://blog.csdn.net/heiyeshuwu/archive/2006/11/13/1380838.aspx
===================================================

命令列檢視Memcached執行狀態

首先登入到伺服器,然後在cmd命令列中鍵入: telnet 127.0.0.1 11211
其中127.0.0.1是伺服器的地址(這裡是本機) ,11211是memcached繫結的埠號。
之後命令列視窗全黑只有游標提示,摸黑輸入stats,即可得到描述Memcached伺服器執行情況的引數。如下圖:

其中,uptime 是memcached執行的秒數,cmd_get是查詢快取的次數。這兩個資料相除一下就能得到平均每秒請求快取的次數——最近niupu的流量很低,所以平均也就一秒請求一次多,這麼點大的壓力,用檔案系統快取一樣沒問題,根本不會體現出使用memcached的優越。

下面的cmd_set 就是設定key=>value的次數。整個memcached是個大hash,用cmd_get沒有找到的內容,就會呼叫一下cmd_set寫進快取裡。緊跟著是get_hits,就是快取命中的次數。快取命中率 = get_hits/cmd_get * 100%。
下面的get_misses的數字加上get_hits應該等於cmd_get。而total_itemscurr_items表示現在在快取中的鍵值對個數,在圖上total_items == cmd_set == get_misses,不過當可用最大記憶體用光時,memcached就會刪掉一些內容,上面的等式就不成立了

話說回來,memcached要是能有一套完整的監測工具就太好了。memcached的安裝和php相應配置請看這裡


相關文章