天天看點

Memcached 記憶體級緩存

Memcached在大型網站中應用

    memcached是一個高性能的分布式的記憶體對象緩存系統,通過在記憶體裡維護一個統一的巨大的hash表,它能夠用來存儲各種格式的資料,包括圖像、視 頻、檔案以及資料庫檢索的結果等。最初為了加速 LiveJournal 通路速度而開發的,後來被很多大型的網站采用。起初作者編寫它可能是為了提高動态網頁應用,為了減輕資料庫檢索的壓力,來做的這個緩存系統。它的緩存是一 種分布式的,也就是可以允許不同主機上的多個使用者同時通路這個緩存系統, 這種方法不僅解決了共享記憶體隻能是單機的弊端, 同時也解決了資料庫檢索的壓力,最大的優點是提高了通路擷取資料的速度!基于memcached作者對分布式cache的了解和解決方案。 memcached完全可以用到其他地方 比如分布式資料庫, 分布式計算等領域。

1、 memcached 協定了解

memcache是為了加快http://www.livejournal.com/通路速度而誕生的一個項目。

它的官方首頁是:http://www.danga.com/memcached/

目前在網站開發中應用較少,主要的應用有:

http://www.danga.com/memcached/users.bml

在國内的網站開發中,還很少沒見到有應用的,中文資料十分匮乏。

工 作機制:通過在記憶體中開辟一塊區域來維持一個大的hash表來加快頁面通路速度,和資料庫是獨立的。但是目前主要用來緩存資料庫的資料。允許多個 server通過網絡形成一個大的hash,使用者不必關心資料存放在哪,隻調用相關接口就可。存放在記憶體的資料通過LRU算法進行淘汰出記憶體。同時可以通 過删除和設定失效時間來淘汰存放在記憶體的資料。

2、 memcached 使用入門

2.1 memcached的安裝

<1>memcached服務的安裝

先檢查linux核心版本,建議将memcached 安裝在2.6以上。

因為memcached 需要用到libevent和 epoll 。

memcached安裝前首先确定你的伺服器上面安裝了libevent庫,

libevent下載下傳位址( http://www.monkey.org/~provos/libevent/)。

下載下傳memcached的源碼( http://www.danga.com/memcached/download.bml)。

Memcached最初是用perl寫的,現在的版本是用c寫的。

下載下傳後拷貝到一個目錄,安裝需要root使用者來執行

tar -zxvf memcached-1.1.12.tar.gz

cd memcached-1.1.12

./configure

這裡必須先要configure, 它會檢測你的系統情況,然後生成一個config.h檔案和其它的幾個檔案,另外和其它的configure一樣,你可以配置它的安裝路徑等等。預設應用程式安裝在/usr/local/bin目錄下。

make //編譯

make install //安裝

<2>memcached用戶端的安裝

根據memcached協定,使用者可以自己寫出符合自己要求的用戶端程式。目前http://www.danga.com/memcached/download.bml

提供perl,c,java,python,php等用戶端程式供下載下傳和參考。下面我就以perl用戶端程式為例說明用戶端的安裝:

下載下傳後拷貝到一個目錄,安裝需要root使用者來執行

tar -zxvf Cache-Memcached-1.14.tar.gz

cd Cache-Memcached-1.14

perl makefile.pl

make

make install

make test

這樣就安裝好了memcahced, 啟動memcached就可使用分布式緩存系統了!

2.2 快速入門

<1> memcached服務的啟動

memcached的啟動非常簡單,它沒有配置檔案,隻要配置好幾個參數就可以使用了。下面我以一個實際應用的例子,具體說明一下:

memcached –d –m 500 -l 64.128.191.151 -p 11211 -vv >>/var/www/kelly/test/logs/memcached_$$.log

啟 動的這個memcached為一個背景守護程序模式(-d), 然後緩存的空間為500M(-m), 監聽(-l)伺服器64.128.191.15的11211号端口(-p).,将日志寫道 /var/www/kelly/test/logs/memcached_$$.log(-vv)。

其實memcached的參數也非常的有限,就下面這幾個:

? -p port number to listen on

? -l interface to listen on, default is INDRR_ANY

? -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 is 64 MB

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

? -c max simultaneous connections, default is 1024

? -k lock down all paged memory

? -v verbose (print errors/warnings while in event loop)

? -vv very verbose (also print client commands/reponses)

? -h print this help and exit

? -i print memcached and libevent license

我們也可以将這個啟動腳本寫道/etc/rc.d或者/erc/rc.local,這樣可以在伺服器啟動時候執行。

<2> memcached用戶端的連接配接

下面我就以perl用戶端程式為例說明用戶端的連接配接:

啟動兩個memcached server

memcached –d –m 500 -l 64.128.191.151 -p 11211 -vv >>/var/www/kelly/test/logs/memcached_$$.log

memcached –d –m 500 -l 64.128.191.151 -p 11212 -vv >>/var/www/kelly/test/logs/memcached_$$.log

perl用戶端程式

#!/usr/bin/perl

use Cache::Memcached;

my $memd = new Cache::Memcached {

\'servers\' => [ "64.128.191.15:11211" , "64.128.191.15:11212"],

};

my $val = $memd->get( "my_key" );

if ( $val )

{

print "Value is \'$val\'\n";

}

# Set a value

$memd->set("my_key", "123");

$memd->disconnect_all();

運作測試

$ perl test-memcache.pl

$ perl test-memcache.pl

Value is \'123\'

可以看到,第一次沒有取得my_key,第二次從memcached中得到my_key的值。

同時通過檢視日志,可以發現的确存儲在兩個memcache server中。

這個簡單的例子,解釋了如何在memcached中存取資料,以及memcache是真正的分布式緩存系統。

當然,這還隻是很簡單的例子,展現不出memcache的優勢,下面将通過一個很具體的例子,給出詳細的應用。

3、 memcached在Zorpia的應用

http://www.zorpia.com 是一個網頁相冊,部落格,交友,論壇的大型網站公司。現在已有超過140萬活躍使用者遍布美國,香港,東南亞,歐洲,澳洲,亞洲等其它地區。每天的通路量都在增長,已成為全世界排名第五的社會生活關系網。

Memcached也采用了memcached來提高網站的通路速度,并且取得了很好的效果,我在負責zorpia的memcached項目時候積累了一些經驗,主要的做法如下:

1) 通過對memcache的perl用戶端進行包裝,定制自己的用戶端。

2) 通過制定符合zorpia規範的hash key命名規範

? ? memcache中需要存儲的内容的key均由string組成。

這個string統一由一個memcache.pm的subroutine來實作。(假設這個subroutine是 get_key() )

? ? memcache中存放兩種形式的資料

(1) result of SQL query :

(2) 普通變量(variable)

這兩種資料的key的組合方式是不相同的,由get_key進行判斷和完成

? ? 關于get_key 和 naming rule

get_key subroutine完成所有memcache key的命名,naming rule也是在它裡邊展現:

(1)輸入參數 -- hash結構,裡邊定義了目前需要存放的資料的資訊

結構

(2)傳回值 -- string,傳回資料的key_name

?必須确定 get_key 的傳入hash的結構,

hash中主要有兩個元素

type --- 定義目前資料結構的類型 ,有 \'var\' , \'sql\'兩種值

object --- 存放目前資料結構的詳細資訊,

當 type eq \'var\'時,object表示變量的名字,該名字由程式員指定

當 type eq \'sql\'時,object包含所存放sql的主要基本資訊,hash結構,也由程式員按照規則制定

## 當variable 資料類型,比較簡單

$var_hash = {

type => \'var\', ## var表示目前類型是 variable

object => \'language\', ## language代表variable的名字

};

生成的key是Zorpia::var| language

## sql 資料

比如select first_name from user where user_id =2那麼hash為

$sql_hash = {

type => \'sql\',

object => {

table => {table2=>"user",}, ## sql 查詢的表

column => {column1=>"first_name",}, ## sql所要查詢的column

condition => { user_id =>"2",}, ## sql條件

},

};

生成的key是Zorpia::sql|user|first_name| user_id =2

get_key subroutine必須對傳入hash進行判斷,對不同類型的資料按照不同的方式組合,形成key,傳回給使用者。這個key,必須保證其唯一性:

比如:所有字母小寫,一些數組在組合成key之前必須首先排序

? ? get_key函數

sub get_key{

my $hash = shift;

return undef unless $hash && ref $hash eq "HASH";

my $type = $hash->{type};

my $key_name;

if ($type eq \'sql\') {

my ($table_key,$column_key,$condition_key);

$table_key=_get_key($hash->{object}->{table});

$column_key=_get_key($hash->{object}->{column});

$condition_key=_get_key($hash->{object}->{condition});

$key_name = join(\'|\',$type,$table_key,$column_key,$condition_key);

#Currently the length limit of a key is set at 250 characters

if (length($key_name)>250)

{

$key_name=substr(0,250,$key_name);

}

}

elsif($type eq \'var\')

{

$key_name = join(\'|\',$type,$hash->{object});

}

return $key_name;

}

sub _get_key

{

my $hash=shift;

return undef unless $hash && ref $hash eq "HASH";

my ($t,$ret,$i);

foreach $i (sort keys %$hash)

{

$i=~s/^\s+|\s+$//g;

$hash->{$i}=~s/^\s+|\s+$//g;

push(@$t,lc("$i=$hash->{$i}"));

}

$ret=join(\':\',sort { $a cmp $b } @$t);

return $ret;

}

3) 制定需要應用memcached的規則

?經常通路的表user,user_details

?合理設定變量在memcached的生存周期

?将活躍使用者的資訊預先導入到memcached

?分别在多台機器上啟動多個memcached服務

?編寫腳本監控memcached服務是否活動

4) User表的具體應用舉例

? 在 select時候

先查詢memcahce裡有沒有,有的話,傳回;否則從資料庫select,在memcache裡設定,傳回。

my $sql_hash = {

type => \'sql\',

object => {

table => {table1=>"user",},

column => {column1=>"user_id",},

condition => {email=>$user_id,},

},

};

my $key=Zorpia::MemCache::get_key($sql_hash);

my $user_id_by_email=Zorpia::MemCache::get($key);

if(!$user_id_by_email)

{

my $sth;

my $query ="select user_id from user where email=?";

$sth = $dbh->prepare($query);

$sth->execute($user_id);

my $user1 = $sth->fetchrow_hashref();

$user_id_by_email=$user1->{\'user_id\'};

Zorpia::MemCache::set($key,$user_id_by_email,1800);

}

?在 update,insert,delete時候

先在資料庫update,insert,delete,在memcache裡設定,傳回。

&Zorpia::DB::data_entry_no_return($dbh,"user","COUNT(*)","$account_information_insert_statement user_id=$current_user_id", "user_id=$current_user_id");

#add by kelly

my $sql_hash = {

type => \'sql\',

object => {

table => {table1=>"user",},

column => {column1=>"user_id",},

condition => {user_id=>$current_user_id,},

},

};

my $key=Zorpia::MemCache::get_key($sql_hash);

my $query = "SELECT *, user_id AS id FROM user WHERE user_id=?";

my $sth_memc = $dbh->prepare($query);

$sth_memc->execute($current_user_id);

my $user_memc = $sth_memc->fetchrow_hashref();

&Zorpia::MemCache::set($key,$user_memc,21600);

4、 memcached的應用展望

使 用了memcached以後, 我發現以前做過的很多的項目都可以應用它提高效率,包括最近做的“大單追蹤”, “數位搜尋”等等。當然既然memcahced是分布式的緩存系統,那麼它就是建立了一個分布式的平台, 我們可以用它來進行分布式的記數, 因為對于一個鍵值key我們可以設定它的數值以及有效期在參數中,另外還可以重新設定這個鍵值的數值。 是以我總結了一下目前可以應用到的地方:

<1>.資料庫檢索結果的緩存,也就是說可以有機的和資料庫結合起來應用,提高效率。

這也是目前memcached用到的最多的地方,比如用于大型網站等。

可以這樣來實作:

打開memcached伺服器連接配接

編寫sql語句, 同時算出它的一個hash key值

擷取這個hash值的memcached儲存資料(get)

如果擷取的這個hash值的資料存在。傳回

否則連接配接資料庫查找

把這個查找結果儲存在memcached中(set),可以設定有效期

傳回查找結果

<2>.分布式計算

<3>.分布式共享資料

總之,memcached的機制比較靈活,可以适用于一切需要分布式緩存資料的地方,随着memcached逐漸為人所知,必将在更多的分布式應用領域大放異彩。

---------------------------------------------------------------------------------------------------------------------------------------------

Memcached是什麼?

Memcached是高性能的,分布式的記憶體對象緩存系統,用于在動态應用中減少資料庫負載,提升通路速度。

Memcached由Danga Interactive開發,用于提升LiveJournal.com通路速度的。LJ每秒動态頁面通路量幾千次,使用者700萬。Memcached将資料庫負載大幅度降低,更好的配置設定資源,更快速通路。

如何使用memcached-Server端?

在服務端運作:

# ./memcached -d -m 2048 -l 10.0.0.40 -p 11211

這将會啟動一個占用2G記憶體的程序,并打開11211端口用于接收請求。由于32位系統隻能處理4G記憶體的尋址,是以在大于4G記憶體使用PAE的32位伺服器上可以運作2-3個程序,并在不同端口進行監聽。

如何使用memcached-Client端?

在應用端包含一個用于描述Client的Class後,就可以直接使用,非常簡單。

PHP Example:

$options["servers"] = array("192.168.1.41:11211", "192.168.1.42:11212");

$options["debug"] = false;

$memc = new MemCachedClient($options);

$myarr = array("one","two", 3);

$memc->set("key_one", $myarr);

$val = $memc->get("key_one");

print $val[0]."\n"; // prints \'one‘

print $val[1]."\n"; // prints \'two‘

print $val[2]."\n"; // prints 3

為什麼不使用資料庫做這些?

暫且不考慮使用什麼樣的資料庫(MS-SQL, Oracle, Postgres, MysQL-InnoDB, etc..), 實作事務(ACID,Atomicity, Consistency, Isolation, and Durability )需要大量開銷,特别當使用到硬碟的時候,這就意味着查詢可能會阻塞。當使用不包含事務的資料庫(例如Mysql-MyISAM),上面的開銷不存在,但 讀線程又可能會被寫線程阻塞。

Memcached從不阻塞,速度非常快。

為什麼不使用共享記憶體?

最初的緩存做法是線上程内對對象進行緩存,但這樣程序間就無法共享緩存,命中率非常低,導緻緩存效率極低。後來出現了共享記憶體的緩存,多個程序或者線程共享同一塊緩存,但畢竟還是隻能局限在一台機器上,多台機器做相同的緩存同樣是一種資源的浪費,而且命中率也比較低。

Memcached Server和Clients共同工作,實作跨伺服器分布式的全局的緩存。并且可以與Web Server共同工作,Web Server對CPU要求高,對記憶體要求低,Memcached Server對CPU要求低,對記憶體要求高,是以可以搭配使用。

Mysql 4.x的緩存怎麼樣?

Mysql查詢緩存不是很理想,因為以下幾點:

當指定的表發生更新後,查詢緩存會被清空。在一個大負載的系統上這樣的事情發生的非常頻繁,導緻查詢緩存效率非常低,有的情況下甚至還不如不開,因為它對cache的管理還是會有開銷。

在32位機器上,Mysql對記憶體的操作還是被限制在4G以内,但memcached可以分布開,記憶體規模理論上不受限制。

Mysql上的是查詢緩存,而不是對象緩存,如果在查詢後還需要大量其它操作,查詢緩存就幫不上忙了。

如果要緩存的資料不大,并且查詢的不是非常頻繁,這樣的情況下可以用Mysql 查詢緩存,不然的話memcached更好。

資料庫同步怎麼樣?

這裡的資料庫同步是指的類似Mysql Master-Slave模式的靠日志同步實作資料庫同步的機制。

你可以分布讀操作,但無法分布寫操作,但寫操作的同步需要消耗大量的資源,而且這個開銷是随着slave伺服器的增長而不斷增長的。

下一步是要對資料庫進行水準切分,進而讓不同的資料分布到不同的資料庫伺服器組上,進而實作分布的讀寫,這需要在應用中實作根據不同的資料連接配接不同的資料庫。

當這一模式工作後(我們也推薦這樣做),更多的資料庫導緻更多的讓人頭疼的硬體錯誤。

Memcached可以有效的降低對資料庫的通路,讓資料庫用主要的精力來做不頻繁的寫操作,而這是資料庫自己控制的,很少會自己阻塞 自己。

Memcached快嗎?

非常快,它使用libevent,可以應付任意數量打開的連接配接(使用epoll,而非poll),使用非阻塞網絡IO,分布式散列對象到不同的伺服器,查詢複雜度是O(1)。

參考資料:

Distributed Caching with Memcached | Linux Journal

http://www.danga.com/

http://www.linuxjournal.com/article/7451