php開發memcached

玻璃窗起霧了發表於2021-02-01


一、memcached 簡介

memcached 是高效能的分散式記憶體快取伺服器。一般的使用目的是,透過快取資料庫查詢結果,減少資料庫訪問次數,以提高動態Web應用的速度、提高可擴充套件

性。它可以應對任意多個連線,使用非阻塞的網路IO。由於它的工作機制是在記憶體中開闢一塊空間,然後建立一個HashTable,Memcached自管理這些HashTable。

二、memcached 安裝(參考”  Linux 下的Memcache安裝 ”)

首先是下載 memcached 了,目前最新版本是 1.4.0,直接從官方網站即可下載到 memcached-1[1].4.0.tar.gz。除此之外,memcached 用到了 libevent,我yum install libevent libevent-devel

接下來是將 memcached-1[1].4.0.tar.gz 解開包、編譯、安裝:

# tar -xzf memcached-1.1.12.tar.gz
# cd memcached-1.1.12
# ./configure
# make
# make install

三、執行 memcached 守護程式

執行 memcached 守護程式很簡單,只需一個命令列即可,不需要修改任何配置檔案(也沒有配置檔案給你修改):

/usr/local/bin/memcached -d -m 128 -l 192.168.x.y -p 11211 -u www

引數解釋:

-d 以守護程式(daemon)方式執行 memcached;
-m 設定 memcached 可以使用的記憶體大小,單位為 M;
-l 設定監聽的 IP 地址,如果是本機的話,通常可以不設定此引數;(一般是不寫)
-p 設定監聽的埠,預設為 11211,所以也可以不設定此引數;
-u 指定使用者,如果當前為 root 的話,需要使用此引數指定使用者。

當然,還有其它引數可以用, man memcached 一下就可以看到了。

四、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 下需編譯安裝memcache,Window 下則在 php.ini 中去掉 php_memcache.dll 前邊的註釋符,使其可用。

參考張宴文章中安裝nginx的一部分

編譯安裝PHP5擴充套件模組

tar zxvf memcache-2.2.5.tgz   <---- 從 下載 (這是一個php擴充套件包資源庫)
cd memcache-2.2.5/
/usr/local/webserver/php/bin/phpize
./configure --with-php-config=/usr/local/webserver/php/bin/php-config
make
make install
cd ../

這樣,php.ini檔案中新增了extension = "memcache.so"

例子( PHP 的Memcache):

(1)

<?php

$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 ."<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的物件:
$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 . "<br>";

最後我們把所有的儲存在Memcache伺服器上的資料都清除,會發現資料都沒有了,最後輸出key2的資料為空,最後關閉連線
$mem->flush();
$val2 = $mem->get(‘key2′);
echo "Get key2 value: ";
print_r($val2);
echo "<br>";

(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 "<table cellpadding=10 border=2>";

    echo "<tr>";

    echo "<th>Uid</th>";

    echo "<th>Username</th>";

    echo "<th>PassWord</th>";

    echo "<th>Gender</th>";

    echo "</tr>";

    for($i=0;$i<10;$i++){

        echo "<tr>";

        for($j=0;$j<4;$j++){

        echo "<td>".$result[$i][$j]."</td>";

        }

        echo "</tr>";

    }     

?>

第二種,可以避開擴充套件、重新編譯所帶來的麻煩,那就是直接使用 php-memcached-client.php,但效率會比擴充套件庫稍差一些。

首先 下載 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.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 "<br>".str_pad('$mc->add() ', 60, '_')."<br>";
var_dump($val);
//  替換已寫入的物件資料值
$mc->replace($key, array('some'=>'haha', 'array'=>'xxx'));
$val = $mc->get($key);
echo "<br>".str_pad('$mc->replace() ', 60, '_')."<br>";
var_dump($val);
//  刪除 memcached 中的物件
$mc->delete($key);
$val = $mc->get($key);
echo "<br>".str_pad('$mc->delete() ', 60, '_')."<br>";
var_dump($val);
?>

是不是很簡單,在實際應用中,通常會把資料庫查詢的結果集儲存到 memcached 中,下次訪問時直接從 memcached 中獲取,而不再做資料庫查詢操作,這樣可以在很大程度上減輕資料庫的負擔。通常會將 SQL 語句 md5() 之後的值作為唯一識別符號 key。下邊是一個利用 memcached 來快取資料庫查詢結果集的示例(此程式碼片段緊接上邊的示例程式碼):

 

<?php
$sql = 'SELECT * FROM users';
$key = md5($sql);   //memcached 物件識別符號
if ( !($datas = $mc->get($key)) ) {
    //  在 memcached 中未獲取到快取資料,則使用資料庫查詢獲取記錄集。
    echo "<br>".str_pad('Read datas from MySQL.', 60, '_')."<br>";
    $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 "<br>".str_pad('Read datas from memcached.', 60, '<br>')."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 。
下載:
另外,Memcache用到了libevent這個庫用於Socket的處理,所以還需要安裝libevent,libevent的最新版本是libevent-1.3。(如果你的系統已經安裝了libevent,可以不用安裝)
官網:~provos/libevent/
下載:~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
# wget ~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.
在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;
?>

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69992957/viewspace-2755245/,如需轉載,請註明出處,否則將追究法律責任。

相關文章