Memcache工作原理总结
<!--[if !supportLists]-->1.? <!--[endif]-->分片原理
?
咱們廢話話不多說了,直接看Memcache的原理。首先memcache解決的最大的一個問題就是內(nèi)存多次讀取的內(nèi)存碎片問題。內(nèi)存碎片分為內(nèi)存內(nèi)部碎片和內(nèi)存外部碎片。一般是指在外部碎片中出現(xiàn)了不連續(xù)的細(xì)小內(nèi)存片段,不能夠被進(jìn)程利用。因為不連續(xù),不能組合成大而的連續(xù)空間,導(dǎo)致這部分空間很可惜的浪費了。內(nèi)存碎片是因為在分配一個內(nèi)存塊后,使之空閑,但不將空閑內(nèi)存歸還給最大內(nèi)存塊而產(chǎn)生的。
?
那么memcache啟動進(jìn)程的時候就按照預(yù)先設(shè)定好的大小(默認(rèn)是64mb)相內(nèi)存開辟出一段連續(xù)的內(nèi)存空間,之后再將這段內(nèi)存空間分成不同的片段。
?
相信下面的圖大家都見過了
?
?
Memcache單進(jìn)程最大可開的內(nèi)存是2GB,如果想緩存更多的數(shù)據(jù),建議還是開辟更多的memcache進(jìn)程(不同端口)或者使用分布式memcache進(jìn)行緩存,將數(shù)據(jù)緩存到不同的物理機或者虛擬機上。
?
Memcache進(jìn)程啟動,在內(nèi)存開辟了連續(xù)的區(qū)域。咱們用上面的圖形來舉例,這段連續(xù)的區(qū)域就好像上面的slab1+slab2+slab3+……+slab(n).分配區(qū)域相同的構(gòu)成了slab(分片組)。Slab下面可不直接就是存儲區(qū)域片(就是圖中的chunks)了。而是page,如果一個新的緩存數(shù)據(jù)要被存放,memcached首先選擇一個合適的slab,然后查看該slab是否還有空閑的chunk,如果有則直接存放進(jìn)去;如果沒有則要進(jìn)行申請。slab申請內(nèi)存時以page為單位,所以在放入第一個數(shù)據(jù),無論大小為多少,都會有1M大小的page被分配給該slab。申請到page后,slab會將這個page的內(nèi)存按chunk的大小進(jìn)行切分,這樣就變成了一個chunk的數(shù)組,在從這個chunk數(shù)組中選擇一個用于存儲數(shù)據(jù)。(http://blog.csdn.net/21aspnet/article/details/7022827)。在Page中才是一個個小存儲單元——chunks,一個page默認(rèn)1mb,那么可以放多少個88字節(jié)單位的chunks呢?1024*1024/88約等于11915個。如果放入記錄是一個100字節(jié)的數(shù)據(jù),那么在88字節(jié)的chunks和112字節(jié)的chunks中如何調(diào)配呢。答案當(dāng)然是緊著大的用,不可能將請求過來的數(shù)據(jù)再做個分解、分離存儲、合并讀取吧。這樣也就帶來了一個小問題,還是有空間浪費掉了。112-100=12字節(jié),這12字節(jié)就浪費了。
?
在緩存的清除方面,memcache是不釋放已分配內(nèi)存。當(dāng)已分配的內(nèi)存所在的記錄失效后,這段以往的內(nèi)存空間,memcache自然會重復(fù)利用起來。至于過期的方式,也是采取get到此段內(nèi)存數(shù)據(jù)的時候采取查詢時間戳,看是否已經(jīng)超時失效?;静粫衅渌€程干預(yù)數(shù)據(jù)的生命周期。至于清空的策略等同于ehcache的默認(rèn)策略——最近很少使用清空策略——也就是英文常用的LRU——Least Recently Used。
?
而memcache鑒定內(nèi)存不足是在什么情況下呢:無法從slab里面獲取新的存儲單元了。這個對內(nèi)存十分貪婪的東東?;痉?wù)器都得是2~4GB以上方能吃得消(非時效性,或者說時效性較低的數(shù)據(jù))。
?
Memcache借助了操作系統(tǒng)的libevent工具做高效的讀寫。libevent是個程序庫,它將Linux的epoll、BSD類操作系統(tǒng)的kqueue等事件處理功能封裝成統(tǒng)一的接口。即使對服務(wù)器的連接數(shù)增加,也能發(fā)揮高性能。memcached使用這個libevent庫,因此能在Linux、BSD、Solaris等操作系統(tǒng)上發(fā)揮其高性能。Memcache號稱可以接受任意數(shù)量的連接請求。事實真的是這樣嗎?
?
<!--[if !supportLists]-->1.? <!--[endif]-->存儲過程分析
假設(shè)我們現(xiàn)在往memcache中存儲一個緩存記錄,首先在使用memcache客戶端程序的時候要制定一個初始化的服務(wù)機器路由表,比如Java的客戶端程序
Java代碼 ??那么在做存儲的時候memcache客戶端程序會hash出一個碼,之后再根據(jù)路由表去將請求轉(zhuǎn)發(fā)給memcache服務(wù)端,也就是說memcache的客戶端程序相當(dāng)于做了一個類似負(fù)載均衡的功能。下面這個圖也是大家以前看過的
?
?
?
而memcache在server上面的進(jìn)程僅僅負(fù)責(zé)監(jiān)聽服務(wù)和接受請求、存儲數(shù)據(jù)的作用。分發(fā)不歸他管。所以這么看的話,散列到每臺memcache服務(wù)機器,讓每臺機器分布存儲得均勻是客戶端代碼實現(xiàn)的一個難點。這個時侯Hash散列算法就顯得格外重要了吧。
?
<!--[if !supportLists]-->1.? <!--[endif]-->讀取過程分析
?
理解了memcache的存儲就不難理解memcache的讀取緩存的過程了。在讀取的時候也是根據(jù)key算出一個hash,之后在算出指定的路由物理機位置,再將請求分發(fā)到服務(wù)機上。
?
?
memcache分布式讀寫的存儲方式有利有弊。如果node2宕機了,那么node2的緩存數(shù)據(jù)就沒了,那么還得先從數(shù)據(jù)庫load出來數(shù)據(jù),重新根據(jù)路由表(此時只有node1和node3),重新請求到一個緩存物理機上,在寫到重定向的緩存機器中。災(zāi)難恢復(fù)已經(jīng)實現(xiàn)得較為完備。弊端就是維護(hù)這么一個高可用緩存,成本有點兒大了。為了存儲更多的數(shù)據(jù),這樣做是否利大于弊,還是得看具體的應(yīng)用場景再定。
?
<!--[if !supportLists]-->1.? <!--[endif]-->與ehcache的爭論
?
Ehcache的爭論之前就說過了,總是在性能上來說這兩個的性能如何。還有大家在網(wǎng)上常見的一個列表進(jìn)行了比較。筆者覺得,這兩個最大的差異是原理的差異決定了應(yīng)用場景的差異。比如做單點應(yīng)用緩存的時候,就可以使用ehcache直接向本地內(nèi)存進(jìn)行緩存的讀寫。而做集群緩存的時候一般是借由一個集中式管理server來做緩存,既然是集中式server就少不了網(wǎng)絡(luò)傳輸了,這個時侯memcache較為適合。不是說ehcache不能做集群式的緩存,而是做了集群的緩存的代價(RMI、JMS、JGroups)、網(wǎng)絡(luò)資源的占用確實比memcache高一些。至于內(nèi)存的讀寫操作效率,這個不太好說。Ehcache用java的隨機讀寫類操作二進(jìn)制的buffer。Memcache底層是基于libevent程序庫的C服務(wù)。這個相信效率都差不多。關(guān)鍵的消耗還是在網(wǎng)絡(luò)IO資源上。
總結(jié)
以上是生活随笔為你收集整理的Memcache工作原理总结的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: MemCache详细解读
- 下一篇: nginx源码分析—内存池结构ngx_p