memcached一些應用點滴

 

 

大名鼎鼎的分佈式緩存系統memcached,在開源社區中可謂是無人不知無人不曉,memcached支持分佈式的橫向擴展,但memcached的服 務端卻是單實例,並無"分佈式"的功能,所謂的分佈式只是客戶端在存儲的主鍵做分佈的存儲;還有memcached組件緩存對象,如果組件無進行序列化必 定無法正確取得數據;如何使用memcached的java組件來監控memcached的運行狀態;以上等等的問題是我在日常的工作中碰到並解決的,拿 出來跟大家做個分享^_^



對象的序列化
首先memcached是獨立的服務器組件,獨立於應用系統,從客戶端保存和讀取對象到memcached是必須通過網絡傳輸,因爲網絡傳輸都是二進制的數據,所以所有的對象都必須經過序列化,否則無法存儲到memcahced的服務器端.
正如我們以往在集羣中應用的序列化一樣,memcached的序列化的性能也是往往讓大家頭疼,如果我們對我們的domain類進行對象的序列化,第一次 序列化時間會比較長,但後續會優化,也就是說序列化最大的消耗不是對象的序列化,而是類的序列化,如果存儲的只是一個String對象,這種情況是最理想 的,省去了序列化的操作.實際上String對象本身已經實現了序列化接口,無法我們再次去進行序列化操作.



memcached的原子加法
記錄一下上次犯得一個錯誤

<%
static   int  count  =   0 ;
count
++ ;
MemCachedClient mcc 
=   new  MemCachedClient();
mcc.add(
" test.html " , count);
%>


這段代碼的作用是將test.html的用戶訪問次數保存到memcached中,粗劣一看好像並無錯誤,但在高併發時的出來的訪問數據一定是小於實際的 訪問數量,當然這裏並不是memcached對象鎖的問題,主要還是程序中線程的同步問題,但是如果使用java的synchronized或lock那 麼在性能上肯定是無法忍受的,memcached客戶端組件帶有原子性的加法和減法

<%
MemCachedClient mcc 
=   new  MemCachedClient();
System.out.println(mcc.addOrIncr(
" test.html " , 1 ));
%>


long addORIncr(String key,long inc)爲計數器值增加inc,如果計數器不存在,則保存inc爲計數器的值,必須注意的是服務器端不會對超過2的32次方的行爲進行檢查




分佈式的mencached
memcached雖然是屬於分佈式的緩存服務器,但實際上memcached服務端之間並無分佈式的功能,不會互相通信共享數據,如何進行分佈式,這完全是取決於客戶端的實現



假設我們現在有三臺memcached服務器分別爲node1,node2,node3,應用程序要保存鍵名分別 爲"test1","test2","test3",客戶端實現的算法就是根據鍵名來決定保存數據的memcached服務器,我們將"test1"保存 到node1,"test2"保存到node2,"test3"保存到node3,並且在讀取緩存數據也是通過一樣的算法從各臺服務器上讀取相應的 key,這樣通過一個最簡單的算法將不同的鍵保存到不同的服務器上,實現了memcached的分佈式.
但是這種算法很難確保每臺服務器得到較爲平均的數據量,我們需要改變一下客戶端的算法,簡單來說,就是根據服務器的臺數的餘數進行分散

<%
"
test1 " .hashCode() % 3
%>


根據key的java.lang.String.hashCode()取得散列值,再將值模服務器的臺數得到餘數值,我們再根據這個餘數值來判定這個 key要存入哪一臺服務器,當key的數量越來越大,對key的散列取模也會趨向平均,基本可以保證幾臺memcached服務器所存儲的緩存量趨向平均
似乎很完美,餘數計算的方法很簡單,數據的分散性也很優秀,但也有其缺點,就是當需要添加或移除服務器時,緩存的重組代價是相當巨大的,添加或移除服務器時,餘數就會發生變化,這樣就無法取到與原來緩存時相同的服務器.
網上介紹的Consistent Hashing算法基本上可以解決這個問題,這裏做個簡單的說明,首先是求出memcached服務器節點的哈希值,並將其配置到0-2的32次方的圓 上,然後用同樣的方法求出存儲數據的鍵的哈希值,並映射到圓上.然後從數據映射到的位置開始順時針查找,將數據保存到找到的第一個服務器上.如果超過2的 32次方仍然找不到服務器,就會保存到第一臺memcached服務器上


從上圖的狀態中添加一臺memcached服務器。餘數分佈式算法由於保存鍵的服務器會發生巨大變化而影響緩存的命中率,但Consistent Hashing中,只有在continuum上增加服務器的地點逆時針方向的第一臺服務器上的鍵會受到影響



幾種連接客戶端的對比
目前java的memcached主要有Java-Memcached-Client,Xmemached,Spymemcached三種,這三個客戶端的性能測試可以看
http://xmemcached.googlecode.com/svn/trunk/benchmark/benchmark.html


請求的資源爲64Bytes,在低併發Java-Memcached-Client是佔有一定的優勢,但在併發數超過100以後,Java- Memcached-Client是呈現直線下跌,併發數達到300已經無法承受,Spymemcached和Xmemached表現相對穩定,特別是 Xmemached無論在低併發或高併發都保持優秀的性能表現



併發數固定爲100時,在小文件的請求Java-Memcached-Client還是佔有優勢,當隨着請求的size越來越大,三者趨向於同一點
如果你對memcached訪問的負載不高,那麼Java-Memcached-Client是一個不錯的選擇,如果你對memcached訪問的負載要 求較高,推薦使用Xmemached,如果需要異步的批量處理,可以選擇Spymemcached,如果你什麼都不知道,那麼建議使用 Xmemached,因爲無論在何種情況,它都可以表現出較好的性能,雖然不是最好



監控memcached
推薦使用nagios或cactis進行監控,nagios沒有配置過,cactis是需要下載一個腳本插件
這裏推薦一個從網上淘來的php,只要把它放到你的機器中,當然你的機器要支持php環境,將此php放入你的網頁訪問網絡就可以訪問
下載
http://www.blogjava.net/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


監控的平臺



理解memcached的刪除機制 memcached內部不會監視記錄是否過期,而是在get時查看記錄的時間戳,檢查記錄是否過期, 這種技術被稱爲lazy(惰性)expiration.因此,memcached不會在過期監視上耗費CPU時間 memcached會優先使用已超時的記錄的空間,並使用LRU算法來分配空間,因此當memcached的內存空間不足,就從最近違背使用的記錄中搜索,並將空間分配給新的記錄 不過在某些情況下LRU機制會造成某些麻煩,如你並不想要淘汰已被緩存過的記錄,可以在memcached啓動時添加 -M 參數來禁止LRU,但這樣在memcached的內存用盡時,memcached會返回錯誤,是否使用LRU,在於你的需求

相關文章
相關標籤/搜索
每日一句
    每一个你不满意的现在,都有一个你没有努力的曾经。