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