大名鼎鼎的分布式緩存系統(tǒng)memcached,在開源社區(qū)中可謂是無人不知無人不曉,memcached支持分布式的橫向擴展,但memcached的服務(wù)端卻是單實例,并無"分布式"的功能,所謂的分布式只是客戶端在存儲的主鍵做分布的存儲;還有memcached組件緩存對象,如果組件無進行序列化必定無法正確取得數(shù)據(jù);如何使用memcached的java組件來監(jiān)控memcached的運行狀態(tài);以上等等的問題是我在日常的工作中碰到并解決的,拿出來跟大家做個分享^_^
對象的序列化
首先memcached是獨立的服務(wù)器組件,獨立于應(yīng)用系統(tǒng),從客戶端保存和讀取對象到memcached是必須通過網(wǎng)絡(luò)傳輸,因為網(wǎng)絡(luò)傳輸都是二進制的數(shù)據(jù),所以所有的對象都必須經(jīng)過序列化,否則無法存儲到memcahced的服務(wù)器端.
正如我們以往在集群中應(yīng)用的序列化一樣,memcached的序列化的性能也是往往讓大家頭疼,如果我們對我們的domain類進行對象的序列化,第一次序列化時間會比較長,但后續(xù)會優(yōu)化,也就是說序列化最大的消耗不是對象的序列化,而是類的序列化,如果存儲的只是一個String對象,這種情況是最理想的,省去了序列化的操作.實際上String對象本身已經(jīng)實現(xiàn)了序列化接口,無法我們再次去進行序列化操作.
memcached的原子加法
記錄一下上次犯得一個錯誤
<%
static int count = 0;
count++;
MemCachedClient mcc = new MemCachedClient();
mcc.add("test.html", count);
%>
這段代碼的作用是將test.html的用戶訪問次數(shù)保存到memcached中,粗劣一看好像并無錯誤,但在高并發(fā)時的出來的訪問數(shù)據(jù)一定是小于實際的訪問數(shù)量,當(dāng)然這里并不是memcached對象鎖的問題,主要還是程序中線程的同步問題,但是如果使用java的synchronized或lock那么在性能上肯定是無法忍受的,memcached客戶端組件帶有原子性的加法和減法
<%
MemCachedClient mcc = new MemCachedClient();
System.out.println(mcc.addOrIncr("test.html",1));
%>
long addORIncr(String key,long inc)為計數(shù)器值增加inc,如果計數(shù)器不存在,則保存inc為計數(shù)器的值,必須注意的是服務(wù)器端不會對超過2的32次方的行為進行檢查
分布式的mencached
memcached雖然是屬于分布式的緩存服務(wù)器,但實際上memcached服務(wù)端之間并無分布式的功能,不會互相通信共享數(shù)據(jù),如何進行分布式,這完全是取決于客戶端的實現(xiàn)
假設(shè)我們現(xiàn)在有三臺memcached服務(wù)器分別為node1,node2,node3,應(yīng)用程序要保存鍵名分別為"test1","test2","test3",客戶端實現(xiàn)的算法就是根據(jù)鍵名來決定保存數(shù)據(jù)的memcached服務(wù)器,我們將"test1"保存到node1,"test2"保存到node2,"test3"保存到node3,并且在讀取緩存數(shù)據(jù)也是通過一樣的算法從各臺服務(wù)器上讀取相應(yīng)的key,這樣通過一個最簡單的算法將不同的鍵保存到不同的服務(wù)器上,實現(xiàn)了memcached的分布式.
但是這種算法很難確保每臺服務(wù)器得到較為平均的數(shù)據(jù)量,我們需要改變一下客戶端的算法,簡單來說,就是根據(jù)服務(wù)器的臺數(shù)的余數(shù)進行分散
<%
"test1".hashCode()%3
%>
根據(jù)key的java.lang.String.hashCode()取得散列值,再將值模服務(wù)器的臺數(shù)得到余數(shù)值,我們再根據(jù)這個余數(shù)值來判定這個key要存入哪一臺服務(wù)器,當(dāng)key的數(shù)量越來越大,對key的散列取模也會趨向平均,基本可以保證幾臺memcached服務(wù)器所存儲的緩存量趨向平均
似乎很完美,余數(shù)計算的方法很簡單,數(shù)據(jù)的分散性也很優(yōu)秀,但也有其缺點,就是當(dāng)需要添加或移除服務(wù)器時,緩存的重組代價是相當(dāng)巨大的,添加或移除服務(wù)器時,余數(shù)就會發(fā)生變化,這樣就無法取到與原來緩存時相同的服務(wù)器.
網(wǎng)上介紹的Consistent Hashing算法基本上可以解決這個問題,這里做個簡單的說明,首先是求出memcached服務(wù)器節(jié)點的哈希值,并將其配置到0-2的32次方的圓上,然后用同樣的方法求出存儲數(shù)據(jù)的鍵的哈希值,并映射到圓上.然后從數(shù)據(jù)映射到的位置開始順時針查找,將數(shù)據(jù)保存到找到的第一個服務(wù)器上.如果超過2的32次方仍然找不到服務(wù)器,就會保存到第一臺memcached服務(wù)器上
從上圖的狀態(tài)中添加一臺memcached服務(wù)器。余數(shù)分布式算法由于保存鍵的服務(wù)器會發(fā)生巨大變化而影響緩存的命中率,但Consistent Hashing中,只有在continuum上增加服務(wù)器的地點逆時針方向的第一臺服務(wù)器上的鍵會受到影響
幾種連接客戶端的對比
目前java的memcached主要有Java-Memcached-Client,Xmemached,Spymemcached三種,這三個客戶端的性能測試可以看
http://xmemcached.googlecode.com/svn/trunk/benchmark/benchmark.html

請求的資源為64Bytes,在低并發(fā)Java-Memcached-Client是占有一定的優(yōu)勢,但在并發(fā)數(shù)超過100以后,Java-Memcached-Client是呈現(xiàn)直線下跌,并發(fā)數(shù)達到300已經(jīng)無法承受,Spymemcached和Xmemached表現(xiàn)相對穩(wěn)定,特別是Xmemached無論在低并發(fā)或高并發(fā)都保持優(yōu)秀的性能表現(xiàn)
并發(fā)數(shù)固定為100時,在小文件的請求Java-Memcached-Client還是占有優(yōu)勢,當(dāng)隨著請求的size越來越大,三者趨向于同一點
如果你對memcached訪問的負載不高,那么Java-Memcached-Client是一個不錯的選擇,如果你對memcached訪問的負載要求較高,推薦使用Xmemached,如果需要異步的批量處理,可以選擇Spymemcached,如果你什么都不知道,那么建議使用Xmemached,因為無論在何種情況,它都可以表現(xiàn)出較好的性能,雖然不是最好
監(jiān)控memcached
推薦使用nagios或cactis進行監(jiān)控,nagios沒有配置過,cactis是需要下載一個腳本插件
這里推薦一個從網(wǎng)上淘來的php,只要把它放到你的機器中,當(dāng)然你的機器要支持php環(huán)境,將此php放入你的網(wǎng)頁訪問網(wǎng)絡(luò)就可以訪問
下載
http://www.aygfsteel.com/Files/dongbule/cacti/memcache.rar
修改php以下幾個選項
define('ADMIN_USERNAME','memcache'); // Admin Username
define('ADMIN_PASSWORD','password'); // Admin Password
$MEMCACHE_SERVERS[] = '192.168.1.100:11211'; // add more as an array
#$MEMCACHE_SERVERS[] = 'mymemcache-server2:11211'; // add more as an array
監(jiān)控的平臺
理解memcached的刪除機制
memcached內(nèi)部不會監(jiān)視記錄是否過期,而是在get時查看記錄的時間戳,檢查記錄是否過期, 這種技術(shù)被稱為lazy(惰性)expiration.因此,memcached不會在過期監(jiān)視上耗費CPU時間
memcached會優(yōu)先使用已超時的記錄的空間,并使用LRU算法來分配空間,因此當(dāng)memcached的內(nèi)存空間不足,就從最近違背使用的記錄中搜索,并將空間分配給新的記錄
不過在某些情況下LRU機制會造成某些麻煩,如你并不想要淘汰已被緩存過的記錄,可以在memcached啟動時添加 -M 參數(shù)來禁止LRU,但這樣在memcached的內(nèi)存用盡時,memcached會返回錯誤,是否使用LRU,在于你的需求
----------------------------------------
by 陳于喆
QQ:34174409
Mail: dongbule@163.com