1. 程式人生 > >Java:Memcached-----memcached實現記憶體快取

Java:Memcached-----memcached實現記憶體快取

ached是danga.com(運營LiveJournal的技術團隊)開發的一套分散式記憶體物件快取系統,用於在動態系統中減少資料庫負載,提升效能。LJ每秒動態頁面訪問量幾千次,使用者700萬。Memcached將資料庫負載大幅度降低,更好的分配資源,更快速訪問。

    關於這個東西,相信很多人都用過,本文意在通過對memcached的實現及程式碼分析,獲得對這個出色的開源軟體更深入的瞭解,並可以根據我們的需要對其進行更進一步的優化。末了將通過對BSM_Memcache擴充套件的分析,加深對 memcached的使用方式理解。。

1.Memcached是什麼

在闡述這個問題之前,我們首先要清楚它“不是什麼”。很多人把它當作和SharedMemory那種形式的儲存載體來使用,雖然memcached使用了同樣的“Key=>Value”方式組織資料,但是它和共享記憶體、APC等本地快取有非常大的區別。Memcached是分散式的,也就是說它不是本地的。它基於網路連線(當然它也可以使用localhost)方式完成服務,本身它是一個獨立於應用的程式或守護程序(Daemon方式)。即Memcached是高效能的,分散式的記憶體物件快取系統,用於在動態應用中減少資料庫負載,提升訪問速度。

Memcached 使用libevent庫實現網路連線服務,理論上可以處理無限多的連線,但是它和Apache不同,它更多的時候是面向穩定的持續連線的,所以它實際的併發能力是有限制的。在保守情況下memcached的最大同時連線數為200,這和Linux執行緒能力有關係,這個數值是可以調整的。關於 libevent可以參考相關文件。 Memcached記憶體使用方式也和APC不同。APC是基於共享記憶體和MMAP的,memcachd有自己的記憶體分配演算法和管理方式,它和共享記憶體沒有關係,也沒有共享記憶體的限制,通常情況下,每個memcached程序可以管理2GB的記憶體空間,如果需要更多的空間,可以增加程序數。

2. Memcached適合什麼場合

在很多時候,memcached都被濫用了,這當然少不了對它的抱怨。我經常在論壇上看見有人發貼,類似於“如何提高效率”,回覆是“用memcached”,至於怎麼用,用在哪裡,用來幹什麼一句沒有。memcached不是萬能的,它也不是適用在所有場合。

Memcached 是“分散式”的記憶體物件快取系統,那麼就是說,那些不需要“分佈”的,不需要共享的,或者乾脆規模小到只有一臺伺服器的應用, memcached不會帶來任何好處,相反還會拖慢系統效率,因為網路連線同樣需要資源,即使是UNIX本地連線也一樣。在我之前的測試資料中顯示, memcached本地讀寫速度要比直接PHP記憶體陣列慢幾十倍,而APC、共享記憶體方式都和直接陣列差不多。可見,如果只是本地級快取,使用 memcached是非常不划算的。

 Memcached在很多時候都是作為資料庫前端cache使用的。因為它比資料庫少了很多SQL解析、磁碟操作等開銷,而且它是使用記憶體來管理資料的,所以它可以提供比直接讀取資料庫更好的效能,在大型系統中,訪問同樣的資料是很頻繁的, memcached可以大大降低資料庫壓力,使系統執行效率提升。另外,memcached也經常作為伺服器之間資料共享的儲媒介,例如在SSO系統中儲存系統單點登陸狀態的資料就可以儲存在memcached中,被多個應用共享。

需要注意的是,memcached使用記憶體管理資料,所以它是易失的,當伺服器重啟,或者memcached程序中止,資料便會丟失,所以 memcached不能用來持久儲存資料。很多人的錯誤理解,memcached的效能非常好,好到了記憶體和硬碟的對比程度,其實memcached使用記憶體並不會得到成百上千的讀寫速度提高,它的實際瓶頸在於網路連線,它和使用磁碟的資料庫系統相比,好處在於它本身非常“輕”,因為沒有過多的開銷和直接的讀寫方式,它可以輕鬆應付非常大的資料交換量,所以經常會出現兩條千兆網路頻寬都滿負荷了,memcached程序本身並不佔用多少CPU資源的情況。

通常的網頁快取方式有動態快取和靜態快取等幾種,在ASP.NET中已經可以實現對頁面區域性進行快取,而使用memcached的快取比 ASP.NET的區域性快取更加靈活,可以快取任意的物件,不管是否在頁面上輸出。而memcached最大的優點是可以分散式的部署,這對於大規模應用來說也是必不可少的要求。

LiveJournal.com使用了memcached在前端進行快取,取得了良好的效果,而像wikipedia,sourceforge等也採用了或即將採用memcached作為快取工具。memcached可以大規模網站應用發揮巨大的作用。

2.1 memcached 的工作原理

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

.2  memcached 安裝

首先是下載 memcached 了,目前最新版本是 1.1.12,直接從官方網站即可下載到 memcached-1.1.12.tar.gz。除此之外,memcached 用到了 libevent,我下載的是 libevent-1.1a.tar.gz

接下來是分別將 libevent-1.1a.tar.gz 和 memcached-1.1.12.tar.gz 解開包、編譯、安裝:

# tar -xzf libevent-1.1a.tar.gz
# cd libevent-1.1a
# ./configure --prefix=/usr
# make
# make install
# cd ..
# tar -xzf memcached-1.1.12.tar.gz
# cd memcached-1.1.12
# ./configure --prefix=/usr
# make
# make install

安裝完成之後,memcached 應該在 /usr/bin/memcached。

3.如何使用memcached-Server端?

在服務端執行:

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

 -d 以守護程式(daemon)方式執行 memcached;-m 設定 memcached 可以使用的記憶體大小,單位為 M;

-l 設定監聽的 IP&nb

sp;地址,如果是本機的話,通常可以不設定此引數;-p 設定監聽的埠,預設為 11211,所以也可以不設定此引數;-u 指定使用者,如果當前為 root 的話,需要使用此引數指定使用者。

這將會啟動一個佔用2G記憶體的程序,並開啟11211埠用於接收請求。由於32位系統只能處理4G記憶體的定址,所以在大於4G記憶體使用PAE的32位伺服器上可以執行2-3個程序,並在不同埠進行監聽。

4. 如何使用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

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

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

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

最初的快取做法是線上程內對物件進行快取,但這樣程序間就無法共享快取,命中率非常低,導致快取效率極低。後來出現了共享記憶體的快取,多個程序或者執行緒共享同一塊快取,但畢竟還是隻能侷限在一臺機器上,多臺機器做相同的快取同樣是一種資源的浪費,而且命中率也比較低。

Memcached Server和Clients共同工作,實現跨伺服器分散式的全域性的快取。並且可以與Web Server共同工作,Web Server對CPU要求高,對記憶體要求低,Memcached Server對CPU要求低,對記憶體要求高,所以可以搭配使用。

7.Mysql 4.x的快取怎麼樣?

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

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

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

Mysql上的是查詢快取,而不是物件快取,如果在查詢後還需要大量其它操作,查詢快取就幫不上忙了。

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

8.資料庫同步怎麼樣?

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

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

下一步是要對資料庫進行水平切分,從而讓不同的資料分佈到不同的資料庫伺服器組上,從而實現分佈的讀寫,這需要在應用中實現根據不同的資料連線不同的資料庫。

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

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

9.Memcached快嗎?

非常快,它使用libevent,可以應付任意數量開啟的連線(使用epoll,而非poll),使用非阻塞網路IO,分散式雜湊物件到不同的伺服器,查詢複雜度是O(1)。

10.memcached的相關抽象類

public abstract class BaseManager<T extends BaseObject> implements Manager<T> ...{       private MemcachedClient memcached;       protected int default_cache_time_second = 3600;         public void setMemcached(MemcachedClient memcached) ...{           this.memcached = memcached;        }        public MemcachedClient getMemcached() ...{           return memcached;       }        public void setDefault_cache_time_second(int default_cache_time_second) ...{           this.default_cache_time_second = default_cache_time_second; 

    } public Object getCacheValueFromMemcached(String key) ...{        return getCacheValueFromMemcached(key, null);    }    public Object getCacheValueFromMemcached(String key, Object obj) ...{        Object new_obj = null;        try ...{            new_obj = memcached.get(key);            } catch (Exception e) ...{            if (logger.isWarnEnabled()) ...{

logger.warn("Failed to get from MeMCache", e);               }           }           return (new_obj != null) ? new_obj : obj;        }        public void setCacheValueToMemcached(String cacheKey, int time_to_live, Serializable obj) ...{            if (null != memcached.get(cacheKey)) ...{               memcached.replace(cacheKey, time_to_live, obj);            } else ...{               memcached.add(cacheKey, time_to_live, obj);           }         }       }

11.service呼叫memcache的一個例子:

public List<Integer> getLastModifyAlbumMember(int limit) ...{        String cacheKey = this.createCachekey(new       Object[]...{"schedule","lastmodifyalbumember",limit});        List list = (List) this.getCacheValueFromMemcached(cacheKey);        List<Integer> list1 = new ArrayList<Integer>();        if(null!=list&&list.size()>0)...{            for (Object aList : list) ...{                list1.add(Integer.parseInt(String.valueOf(aList)));            }        }        return list1;    }