1. 程式人生 > >Memcache工作原理總結

Memcache工作原理總結

<!--[if !supportLists]-->1.<!--[endif]-->分片原理

咱們廢話話不多說了,直接看Memcache的原理。首先memcache解決的最大的一個問題就是記憶體多次讀取的記憶體碎片問題。記憶體碎片分為記憶體內部碎片和記憶體外部碎片。一般是指在外部碎片中出現了不連續的細小記憶體片段,不能夠被程序利用。因為不連續,不能組合成大而的連續空間,導致這部分空間很可惜的浪費了。記憶體碎片是因為在分配一個記憶體塊後,使之空閒,但不將空閒記憶體歸還給最大記憶體塊而產生的。

那麼memcache啟動程序的時候就按照預先設定好的大小(預設是64mb)相記憶體開闢出一段連續的記憶體空間,之後再將這段記憶體空間分成不同的片段。

相信下面的圖大家都見過了



 

Memcache單程序最大可開的記憶體是2GB,如果想快取更多的資料,建議還是開闢更多的memcache程序(不同埠)或者使用分散式memcache進行快取,將資料快取到不同的物理機或者虛擬機器上。

Memcache程序啟動,在記憶體開闢了連續的區域。咱們用上面的圖形來舉例,這段連續的區域就好像上面的slab1+slab2+slab3+……+slab(n).分配區域相同的構成了slab(分片組)。Slab下面可不直接就是儲存區域片(就是圖中的chunks)了。而是page,如果一個新的快取資料要被存放,memcached首先選擇一個合適的slab

,然後檢視該slab是否還有空閒的chunk,如果有則直接存放進去;如果沒有則要進行申請。slab申請記憶體時以page為單位,所以在放入第一個資料,無論大小為多少,都會有1M大小的page被分配給該slab。申請到page後,slab會將這個page的記憶體按chunk的大小進行切分,這樣就變成了一個chunk的陣列,在從這個chunk陣列中選擇一個用於儲存資料。位元組的資料,那麼在88位元組的chunks112位元組的chunks中如何調配呢。答案當然是緊著大的用,不可能將請求過來的資料再做個分解、分離儲存、合併讀取吧。這樣也就帶來了一個小問題,還是有空間浪費掉了。112-100=12位元組,這12
位元組就浪費了。

在快取的清除方面,memcache是不釋放已分配記憶體。當已分配的記憶體所在的記錄失效後,這段以往的記憶體空間,memcache自然會重複利用起來。至於過期的方式,也是採取get到此段記憶體資料的時候採取查詢時間戳,看是否已經超時失效。基本不會有其他執行緒干預資料的生命週期。至於清空的策略等同於ehcache的預設策略——最近很少使用清空策略——也就是英文常用的LRU——Least Recently Used

memcache鑑定記憶體不足是在什麼情況下呢:無法從slab裡面獲取新的儲存單元了。這個對記憶體十分貪婪的東東。基本伺服器都得是2~4GB以上方能吃得消(非時效性,或者說時效性較低的資料)。

Memcache藉助了作業系統的libevent工具做高效的讀寫。libevent是個程式庫,它將LinuxepollBSD類作業系統的kqueue等事件處理功能封裝成統一的介面。即使對伺服器的連線數增加,也能發揮高效能。memcached使用這個libevent庫,因此能在LinuxBSDSolaris等作業系統上發揮其高效能。Memcache號稱可以接受任意數量的連線請求。事實真的是這樣嗎?

<!--[if !supportLists]-->1.<!--[endif]-->儲存過程分析

假設我們現在往memcache中儲存一個快取記錄,首先在使用memcache客戶端程式的時候要制定一個初始化的服務機器路由表,比如Java的客戶端程式

Java程式碼  收藏程式碼
  1. cachedClient = new MemCachedClient();    
  2. //獲取連線池例項  
  3. SockIOPool pool = SockIOPool.getInstance();    
  4. //設定快取伺服器地址,可以設定多個實現分散式快取  
  5. pool.setServers(new String[]{"127.0.0.1:11211","192.176.17.90:11211"});  

 那麼在做儲存的時候memcache客戶端程式會hash出一個碼,之後再根據路由表去將請求轉發給memcache服務端,也就是說memcache的客戶端程式相當於做了一個類似負載均衡的功能。下面這個圖也是大家以前看過的



 

memcacheserver上面的程序僅僅負責監聽服務和接受請求、儲存資料的作用。分發不歸他管。所以這麼看的話,雜湊到每臺memcache服務機器,讓每臺機器分佈儲存得均勻是客戶端程式碼實現的一個難點。這個時侯Hash雜湊演算法就顯得格外重要了吧。

<!--[if !supportLists]-->1.<!--[endif]-->讀取過程分析

理解了memcache的儲存就不難理解memcache的讀取快取的過程了。在讀取的時候也是根據key算出一個hash,之後在算出指定的路由物理機位置,再將請求分發到服務機上。



 

memcache分散式讀寫的儲存方式有利有弊。如果node2宕機了,那麼node2的快取資料就沒了,那麼還得先從資料庫load出來資料,重新根據路由表(此時只有node1node3),重新請求到一個快取物理機上,在寫到重定向的快取機器中。災難恢復已經實現得較為完備。弊端就是維護這麼一個高可用快取,成本有點兒大了。為了儲存更多的資料,這樣做是否利大於弊,還是得看具體的應用場景再定。

<!--[if !supportLists]-->1.<!--[endif]-->ehcache的爭論

Ehcache的爭論之前就說過了,總是在效能上來說這兩個的效能如何。還有大家在網上常見的一個列表進行了比較。筆者覺得,這兩個最大的差異是原理的差異決定了應用場景的差異。比如做單點應用快取的時候,就可以使用ehcache直接向本地記憶體進行快取的讀寫。而做叢集快取的時候一般是藉由一個集中式管理server來做快取,既然是集中式server就少不了網路傳輸了,這個時侯memcache較為適合。不是說ehcache不能做叢集式的快取,而是做了叢集的快取的代價(RMIJMSJGroups)、網路資源的佔用確實比memcache高一些。至於記憶體的讀寫操作效率,這個不太好說。Ehcachejava的隨機讀寫類操作二進位制的bufferMemcache底層是基於libevent程式庫的C服務。這個相信效率都差不多。關鍵的消耗還是在網路IO資源上。