MenberCache學習總結

緣起: 在數據驅動的web開發中,經常要重複從數據庫中取出相同的數據,這種重複極大的增加了數據庫負載。緩存是解決這個問題的好辦法。

Memcache 是什麼?
Memcached是由Danga Interactive開發的,高性能的,分佈式的內存對象緩存系統,用於在動態應用中減少數據庫負載,提升訪問速度。
Memcache 是 danga.com 的一個項目,最早是爲 LiveJournal 服務的,目前全世界不少人使用這個緩存項目來構建自己大負載的網站,來分擔數據庫的壓力。
它可以應對任意多個連接,使用非阻塞的網絡 IO 。由於它的工作機制是在內存中開闢一塊空間,然後建立一個 HashTable , Memcached 自管理這些 HashTable 。

非阻塞的網絡 IO :
(有很多人把阻塞認爲是同步,把非阻塞認爲是異步;個人認爲這樣是不準確的,當然從思想上可以這樣類比,但方式是完全不同的,下面說說在JAVA裏面阻塞IO和非阻塞IO的區別
在JDK1.4中引入了一個NIO的類庫,使得Java涉及IO的操作擁有阻塞式和非阻塞式兩種,問一下阻塞IO與非阻塞IO有什麼區別?有什麼優缺點?
在阻塞模式下,若從網絡流中讀取不到指定大小的數據量,阻塞IO就在那裏阻塞着。比如,已知後面會有10個字節的數據發過來,但是我現在只收到8個字節,那麼當前線程就在那傻傻地等到下一個字節的到來,對,就在那等着,啥事也不做,直到把這10個字節讀取完,這纔將阻塞放開通行。
在非阻塞模式下,若從網絡流中讀取不到指定大小的數據量,非阻塞IO就立即通行。比如,已知後面會有10個字節的數據發過來,但是我現在只收到8個字節,那麼當前線程就讀取這8個字節的數據,讀完後就立即返回,等另外兩個字節再來的時候再去讀取。
從上面可以看出,阻塞IO在性能方面是很低下的,如果要使用阻塞IO完成一個Web服務器的話,那麼對於每一個請求都必須啓用一個線程進行處理。而使用非阻塞IO的話,一到兩個線程基本上就夠了,因爲線程不會產生阻塞,好比一下接收A請求的數據,另一下接收B請求的數據,等等,就是不停地東奔西跑,直接到把數據接收完了。
雖然說,非阻塞IO比阻塞IO有更高的性能,但是對於開發來的,難度就成數倍遞增了。由於是有多少數據就讀取多少數據,這樣在讀取完整之前需要將已經讀取到的數據保存起來,而且需要與其他地方來的數據隔離開來不能混在一起,否則就不知道這數據是誰的了)

Memcache是高性能的分佈式內存緩存服務器。 一般的使用目的是,通過緩存數據庫查詢結果,減少數據庫訪問次數,以提高動態Web應用的速度、 提高可擴展性。
爲什麼會有 Memcache 和 memcached 兩種名稱?
其實 Memcache 是這個項目的名稱,而 memcached 是它服務器端的主程序文件名,
Memcache 官方網站: http://www.danga.com/memcached

Memcache 工作原理
首先 memcached 是以守護程序方式運行於一個或多個服務器中,隨時接受客戶端的連接操作,客戶端可以由各種語言編寫,目前已知的客戶端 API 包括 Perl/PHP/Python/Ruby/Java/C#/C 等等。客戶端在與memcached 服務建立連接之後,接下來的事情就是存取對象了,每個被存取的對象都有一個唯一的標識符key ,存取操作均通過這個 key 進行,保存到 memcached 中的對象實際上是放置內存中的,並不是保存在cache 文件中的,這也是爲什麼 memcached 能夠如此高效快速的原因。注意,這些對象並不是持久的,服務停止之後,裏邊的數據就會丟失。
與許多 cache 工具類似, Memcached 的原理並不複雜。它採用了 C/S 的模式,在 server 端啓動服務進程,在啓動時可以指定監聽的 ip ,自己的端口號,所使用的內存大小等幾個關鍵參數。一旦啓動,服務就一直處於可用狀態。Memcached 的目前版本是通過 C 實現,採用了單進程,單線程,異步 I/O ,基於事件 (event_based) 的服務方式 . 使用libevent 作爲事件通知實現。多個 Server 可以協同工作,但這些 Server 之間是沒有任何通訊聯繫的,每個 Server 只是對自己的數據進行管理。 Client 端通過指定 Server 端的 ip 地址 ( 通過域名應該也可以 ) 。需要緩存的對象或數據是以 key->value 對的形式保存在 Server 端。 key 的值通過 hash 進行轉換,根據 hash 值把 value 傳遞到對應的具體的某個 Server上。當需要獲取對象數據時,也根據 key 進行。首先對 key 進行 hash ,通過獲得的值可以確定它被保存在了哪臺 Server上,然後再向該 Server 發出請求。 Client 端只需要知道保存 hash(key) 的值在哪臺服務器上就可以了。
其實說到底, memcache 的工作就是在專門的機器的內存裏維護一張巨大的 hash 表,來存儲經常被讀寫的一些數組與文件,從而極大的提高網站的運行效率。

memcached的特徵
memcached作爲高速運行的分佈式緩存服務器,具有以下的特點。
協議簡單
基於libevent的事件處理
內置內存存儲方式
memcached不互相通信的分佈式

協議簡單
memcached的服務器客戶端通信並不使用複雜的XML等格式, 而使用簡單的基於文本行的協議。因此,通過telnet 也能在memcached上保存數據、取得數據。下面是例子。
$ telnet localhost 11211

Trying 127.0.0.1...
Connected to localhost.localdomain (127.0.0.1).
Escape character is '^]'.
set foo 0 0 3     (保存命令)
bar               (數據)
STORED            (結果)
get foo           (取得命令)
VALUE foo 0 3     (數據)
bar               (數據)

協議文檔位於memcached的源代碼內,也可以參考以下的URL。

基於libevent的事件處理
libevent是個程序庫,它將Linux的epoll、BSD類操作系統的kqueue等事件處理功能 封裝成統一的接口。即使對服務器的連接數增加,也能發揮O(1)的性能。 memcached使用這個libevent庫,因此能在Linux、BSD、Solaris等操作系統上發揮其高性能。 關於事件處理這裏就不再詳細介紹,可以參考Dan Kegel的The C10K Problem。

libevent

The C10K Problem

內置內存存儲方式
爲了提高性能,memcached中保存的數據都存儲在memcached內置的內存存儲空間中。 由於數據僅存在於內存中,因此重啓memcached、重啓操作系統會導致全部數據消失。 另外,內容容量達到指定值之後,就基於LRU(Least Recently Used)算法自動刪除不使用的緩存。 memcached本身是爲緩存而設計的服務器,因此並沒有過多考慮數據的永久性問題

memcached不互相通信的分佈式
memcached儘管是“分佈式”緩存服務器,但服務器端並沒有分佈式功能。 各個memcached不會互相通信以共享信息。那麼,怎樣進行分佈式呢? 這完全取決於客戶端的實現

Memcached能緩存什麼?
通過在內存裏維護一個統一的巨大的hash表,Memcached能夠用來存儲各種格式的數據,包括圖像、視頻、文件以及數據庫檢索的結果等。
Memcached快麼?
非常快。Memcached使用了libevent(如果可以的話,在linux下使用epoll)來均衡任何數量的打開鏈接,使用非阻塞的網絡I/O, 對內部對象實現引用計數(因此,針對多樣的客戶端,對象可以處在多樣的狀態), 使用自己的頁塊分配器和哈希表, 因此虛擬內存不會產生碎片並且虛擬內存分配的時間複雜度可以保證爲O(1).。
Danga Interactive爲提升Danga Interactive的速度研發了Memcached。目前,LiveJournal.com每天已經在向一百萬用戶提供多達兩千萬次的頁面訪問。而這 些,是由一個由web服務器和數據庫服務器組成的集羣完成的。Memcached幾乎完全放棄了任何數據都從數據庫讀取的方式,同時,它還縮短了用戶查看 頁面的速度、更好的資源分配方式,以及Memcache失效時對數據庫的訪問速度。
Memcached的特點
Memcached的緩存是一種分佈式的,可以讓不同主機上的多個用戶同時訪問, 因此解決了共享內存只能單機應用的侷限,更不會出現使用數據庫做類似事情的時候,磁盤開銷和阻塞的發生。

Memcached的使用
一 、Memcached服務器端的安裝 (此處將其作爲系統服務安裝)
下載文件:memcached 1.2.1 for Win32 binaries (Dec 23, 2006)
1 解壓縮文件到c:\memcached
2 命令行輸入 ‘c:\memcached\memcached.exe -d install’
3 命令行輸入 ‘c:\memcached\memcached.exe -d start’ ,該命令啓動 Memcached ,默認監聽端口爲 11211
通過 memcached.exe -h 可以查看其幫助
ps:安裝時報錯誤的話,如果是WIN7系統,那麼用管理員身份進入CMD模式,找到CMD.EXE,用右鍵選擇管理員模式進入就可以了。

memcached.exe -p 11211 -m 64m -vv
-p 使用的TCP端口。默認爲11211
-m 最大內存大小。默認爲64M
-vv 用very vrebose模式啓動,調試信息和錯誤輸出到控制檯
-d 作爲daemon在後臺啓動

-d restart 重啓memcached服務
-d stop|shutdown 關閉正在運行的memcached服務
-M 內存耗盡時返回錯誤,而不是刪除項
-c 最大同事連接數,默認是1024
-f 塊大小增長因子,默認是1.25
-n 最小分配空間 key+value+flags默認是48
-h 顯示幫助
二、客戶端使用
下載memcached java client:http://www.whalin.com/memcached/#download
1 解壓後將java_memcached-release_2.0.1.jar jar包添加到工程的classpath中
2 利用memcached java client 一個簡單的應用

public class TestMemberCache {

// 創建全局的唯一實例
protected static MemCachedClient mcc = new MemCachedClient();

static {
// 服務器列表和其權重
String[] servers = { "127.0.0.1:11211" };
Integer[] weights = { 3 };

// 獲取socke連接池的實例對象
SockIOPool pool = SockIOPool.getInstance();

// 設置服務器信息
pool.setServers(servers);
pool.setWeights(weights);

// 設置初始連接數、最小和最大連接數以及最大處理時間
pool.setInitConn(5);
pool.setMinConn(5);
pool.setMaxConn(250);
pool.setMaxIdle(1000 * 60 * 60 * 6);

// 設置主線程的睡眠時間
pool.setMaintSleep(30);

// Tcp的規則就是在發送一個包之前,本地機器會等待遠程主機
// 對上一次發送的包的確認信息到來;這個方法就可以關閉套接字的緩存,
// 以至這個包準備好了就發;

// 設置TCP的參數,連接超時等
pool.setNagle(false);
pool.setSocketTO(3000);
pool.setSocketConnectTO(0);

// 初始化連接池
pool.initialize();

// 壓縮設置,超過指定大小(單位爲K)的數據都會被壓縮
mcc.setCompressEnable(true);
mcc.setCompressThreshold(64 * 1024);
}

public static void bulidCache() {
// set(key,value,Date) ,Date是一個過期時間,如果想讓這個過期時間生效的話,這裏傳遞的new Date(long
// date) 中參數date,需要是個大於或等於1000的值。
// 因爲java client的實現源碼裏是這樣實現的 expiry.getTime() / 1000 ,也就是說,如果
// 小於1000的值,除以1000以後都是0,即永不過期
mcc.set("test", "This is a test String", new Date(10000));
mcc.set("test", "This is a test String111", new Date(10000));// 十秒後過期

User u = new User();
u.setUsername("aaaa");
mcc.set("user", u);
User u1 = (User) mcc.get("user");
System.out.println(u1.getUsername());
// add 當這個 key 不存在的時候才保存 value
// replace 當 key 相同的時候才替換 value
// set 直接寫入新的 value ,如果 key 存在就是替換 value

}

public static void output() {
// 從cache裏取值
String value = (String) mcc.get("test");
System.out.println(value);
}

public static void main(String[] args) {

bulidCache();
output();
}
}

輸出結果爲:
aaaa
This is a test String111
MemberCache及其他緩存等相關知識:
1)JBOSS CACHE, EHCACHE, 等JAVA寫成的CACHE,一般都是和主程序在同一機器上,內存直接訪問,比memcached要快。但如果有多個server,每個server都有 自己的一份cache,要採用一些notification or replicate機制才能synchronize。而且,cache size受制於heap size setting。memcached 運行在主程序以外的機器上,通過網絡訪問來傳遞數據。因爲是專門的server,所以size不受限制。如果cache data 不大(300MB以下?),無須用memcached.
2)memcached組成它的n臺機器裏有一個down了,並不會整個掛掉,只會訪問某些cache內容無法命中,就算全部掛掉,Cache就是減少對數據庫訪問的,所以無非就是對數據庫壓力大一些而已,如果希望Cache持久化,或者帶有故障切換功能,可以用memcachedb。
3)Cache Server的可靠性比DB Server還要高
4)memcached 要求set的對象必須是可序列化對象,jboss cache等java obect cache是沒有這個說法的,這是本質的不同的,但是他可以在網絡上用,所以必須序列化也可理解
5)memcached 併發連接可以上到1w,我手頭的應用常常保持在3-5k;它的快不僅是因爲用了libevent,還因爲它採取了“用內存冗餘換存取速度”的內存管理策 略,網上有文章專門分析它的內存分配回收管理的源碼,講的很清楚,在這上面jboss cache、ehcache、oscache跟它沒法比;memcached的集羣也非常好,聽說國外有200+的memcached集羣,我們也有這方 面的嘗試,效果也很好,一臺down掉根本不會引起其他機器down掉,只是這臺的數據丟了,需要慢慢積累回來;而且支持多客戶端,java、php、 python、ruby可以共享數據,就把它當作數據庫用。
我的建議是:你的應用訪問量比較大,對響應速度要求很高,對數據一致性要求一般時,用它,擋在數據庫前面,非常爽(memcached是互聯網公司開發的,正好滿足這三個條件);如果應用不忙,用用ehcache就行了。
6)ehcache、oscache 的數據都是在本機服務器上的,訪問時走的僅是系統總線。而memcached走的是網絡。關於傳輸速度來講我們的應用讀取memcached的網絡流量是每秒有2MB的流量,是讀取數據庫的網絡流量的大約5倍。但是你要知道現在隨便的PC機都是千兆網卡,因此 memcached的get/set操作的延時非常少,並不比echache的get/set慢多少。在一個完整的web應用當中,我的壓力測試表明,性 能差異 <= 5%
7)論壇、sns這樣的應用,會使用多種技術進行緩存。
拿sohu的bbs來說吧,pv爲5000w,峯值8000w
其中帖子、評論讀寫頻繁,其他部分讀頻繁。
帖子列表、評論列表使用c開發(其中排序算法很巧妙),socket調用
帖子、評論內容使用squid緩存
其他讀頻繁的部分使用memcached、squid、定時生成靜態頁面等多種技術。
數據庫用mysql,分表。
個人認爲,小規模應用中,jvm級別的cache可以用用,memcached可用可不用
大規模網站應用,肯定是系統水平切分,多種cache結合。
8)memcached非常快,但我沒說過比本機的ehcache還快,但用本地緩存有兩點不爽:
1.緩存放在內存or放在磁盤?應用重啓會導致內存緩存丟失,放在磁盤又不夠快。內存開多大合適?如果是大訪問量應用,緩存對象集中淘汰可能引起服務器load急劇波動(我們吃過虧,現在也開ehcache,但緩存對象的上限開的很小)
2.集羣應用裏緩存對象如何共享?jboss cache用的是廣播,訪問量大的時候,可以把你的服務拖死,這個我們也吃過虧
當然,有人談到了sohu的例子,一旦訪問量大了,各種緩存都得用着,目前我們就是squid + memcached + ehcache。squid也是好東西,但緩存內容刪除不太靈活,比較適合web1.0,比如新浪搜狐的新聞
另外,java memcached client用1.6好了,沒必要升級到2.1,2.X似乎還不太穩定。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章