MemCache是一個(gè)自由毫胜、源碼開放箕憾、高性能伴栓、分布式的分布式內(nèi)存對(duì)象緩存系統(tǒng)伦连,用于動(dòng)態(tài)Web應(yīng)用以減輕數(shù)據(jù)庫的負(fù)載。它通過在內(nèi)存中緩存數(shù)據(jù)和對(duì)象來減少讀取數(shù)據(jù)庫的次數(shù)钳垮,從而提高了網(wǎng)站訪問的速度惑淳。MemCaChe是一個(gè)存儲(chǔ)鍵值對(duì)的HashMap,在內(nèi)存中對(duì)任意的數(shù)據(jù)(比如字符串饺窿、對(duì)象等)所使用的key-value存儲(chǔ)歧焦,數(shù)據(jù)可以來自數(shù)據(jù)庫調(diào)用、API調(diào)用短荐,或者頁面渲染的結(jié)果倚舀。
MemCache訪問模型
特別澄清一個(gè)問題,MemCache雖然被稱為”分布式緩存”忍宋,但是MemCache本身完全不具備分布式的功能痕貌,MemCache集群之間不會(huì)相互通信,所謂的”分布式”糠排,完全依賴于客戶端程序的實(shí)現(xiàn)舵稠,就像上面這張圖的流程一樣。
同時(shí)基于這張圖,理一下MemCache一次寫緩存的流程:
1哺徊、應(yīng)用程序輸入需要寫緩存的數(shù)據(jù)
2室琢、API將Key輸入路由算法模塊,路由算法根據(jù)Key和MemCache集群服務(wù)器列表得到一臺(tái)服務(wù)器編號(hào)
3落追、由服務(wù)器編號(hào)得到MemCache及其的ip地址和端口號(hào)
4盈滴、API調(diào)用通信模塊和指定編號(hào)的服務(wù)器通信,將數(shù)據(jù)寫入該服務(wù)器轿钠,完成一次分布式緩存的寫操作
讀緩存和寫緩存一樣巢钓,只要使用相同的路由算法和服務(wù)器列表,只要應(yīng)用程序查詢的是相同的Key疗垛,MemCache客戶端總是訪問相同的客戶端去讀取數(shù)據(jù)症汹,只要服務(wù)器中還緩存著該數(shù)據(jù),就能保證緩存命中贷腕。
這種MemCache集群的方式也是從分區(qū)容錯(cuò)性的方面考慮的背镇,假如Node2宕機(jī)了,那么Node2上面存儲(chǔ)的數(shù)據(jù)都不可用了泽裳,此時(shí)由于集群中Node0和Node1還存在瞒斩,下一次請(qǐng)求Node2中存儲(chǔ)的Key值的時(shí)候,肯定是沒有命中的诡壁,這時(shí)先從數(shù)據(jù)庫中拿到要緩存的數(shù)據(jù)济瓢,然后路由算法模塊根據(jù)Key值在Node0和Node1中選取一個(gè)節(jié)點(diǎn),把對(duì)應(yīng)的數(shù)據(jù)放進(jìn)去妹卿,這樣下一次就又可以走緩存了,這種集群的做法很好蔑鹦,但是缺點(diǎn)是成本比較大夺克。
從上面的圖中,可以看出一個(gè)很重要的問題嚎朽,就是對(duì)服務(wù)器集群的管理铺纽,路由算法至關(guān)重要,就和負(fù)載均衡算法一樣哟忍,路由算法決定著究竟該訪問集群中的哪臺(tái)服務(wù)器狡门,先看一個(gè)簡(jiǎn)單的路由算法。
余數(shù)Hash
比方說锅很,字符串str對(duì)應(yīng)的HashCode是50其馏、服務(wù)器的數(shù)目是3,取余數(shù)得到1爆安,str對(duì)應(yīng)節(jié)點(diǎn)Node1叛复,所以路由算法把str路由到Node1服務(wù)器上。由于HashCode隨機(jī)性比較強(qiáng),所以使用余數(shù)Hash路由算法就可以保證緩存數(shù)據(jù)在整個(gè)MemCache服務(wù)器集群中有比較均衡的分布褐奥。
如果不考慮服務(wù)器集群的伸縮性(什么是伸縮性咖耘,請(qǐng)參見大型網(wǎng)站架構(gòu)學(xué)習(xí)筆記),那么余數(shù)Hash算法幾乎可以滿足絕大多數(shù)的緩存路由需求撬码,但是當(dāng)分布式緩存集群需要擴(kuò)容的時(shí)候儿倒,就難辦了。
一致性Hash算法
一致性Hash算法通過一個(gè)叫做一致性Hash環(huán)的數(shù)據(jù)結(jié)構(gòu)實(shí)現(xiàn)Key到緩存服務(wù)器的Hash映射呜笑,看一下我自己畫的一張圖:
具體算法過程為:先構(gòu)造一個(gè)長(zhǎng)度為232的整數(shù)環(huán)(這個(gè)環(huán)被稱為一致性Hash環(huán))义桂,根據(jù)節(jié)點(diǎn)名稱的Hash值(其分布為[0, 232-1])將緩存服務(wù)器節(jié)點(diǎn)放置在這個(gè)Hash環(huán)上,然后根據(jù)需要緩存的數(shù)據(jù)的Key值計(jì)算得到其Hash值(其分布也為[0, 232-1])蹈垢,然后在Hash環(huán)上順時(shí)針查找距離這個(gè)Key值的Hash值最近的服務(wù)器節(jié)點(diǎn)慷吊,完成Key到服務(wù)器的映射查找。
就如同圖上所示曹抬,三個(gè)Node點(diǎn)分別位于Hash環(huán)上的三個(gè)位置溉瓶,然后Key值根據(jù)其HashCode,在Hash環(huán)上有一個(gè)固定位置谤民,位置固定下之后堰酿,Key就會(huì)順時(shí)針去尋找離它最近的一個(gè)Node,把數(shù)據(jù)存儲(chǔ)在這個(gè)Node的MemCache服務(wù)器中张足。使用Hash環(huán)如果加了一個(gè)節(jié)點(diǎn)會(huì)怎么樣触创,看一下:
看到我加了一個(gè)Node4節(jié)點(diǎn),只影響到了一個(gè)Key值的數(shù)據(jù)为牍,本來這個(gè)Key值應(yīng)該是在Node1服務(wù)器上的哼绑,現(xiàn)在要去Node4了。采用一致性Hash算法碉咆,的確也會(huì)影響到整個(gè)集群抖韩,但是影響的只是加粗的那一段而已,相比余數(shù)Hash算法影響了遠(yuǎn)超一半的影響率疫铜,這種影響要小得多茂浮。更重要的是,集群中緩存服務(wù)器節(jié)點(diǎn)越多壳咕,增加節(jié)點(diǎn)帶來的影響越小席揽,很好理解。換句話說谓厘,隨著集群規(guī)模的增大幌羞,繼續(xù)命中原有緩存數(shù)據(jù)的概率會(huì)越來越大,雖然仍然有小部分?jǐn)?shù)據(jù)緩存在服務(wù)器中不能被讀到庞呕,但是這個(gè)比例足夠小新翎,即使訪問數(shù)據(jù)庫程帕,也不會(huì)對(duì)數(shù)據(jù)庫造成致命的負(fù)載壓力。
至于具體應(yīng)用地啰,這個(gè)長(zhǎng)度為232的一致性Hash環(huán)通常使用二叉查找樹實(shí)現(xiàn)愁拭,至于二叉查找樹,就是算法的問題了亏吝,可以自己去查詢相關(guān)資料岭埠。
MemCache實(shí)現(xiàn)原理
首先要說明一點(diǎn),MemCache的數(shù)據(jù)存放在內(nèi)存中蔚鸥,存放在內(nèi)存中個(gè)人認(rèn)為意味著幾點(diǎn):
1惜论、訪問數(shù)據(jù)的速度比傳統(tǒng)的關(guān)系型數(shù)據(jù)庫要快,因?yàn)镺racle止喷、MySQL這些傳統(tǒng)的關(guān)系型數(shù)據(jù)庫為了保持?jǐn)?shù)據(jù)的持久性馆类,數(shù)據(jù)存放在硬盤中,IO操作速度慢
2弹谁、MemCache的數(shù)據(jù)存放在內(nèi)存中同時(shí)意味著只要MemCache重啟了乾巧,數(shù)據(jù)就會(huì)消失
3、既然MemCache的數(shù)據(jù)存放在內(nèi)存中预愤,那么勢(shì)必受到機(jī)器位數(shù)的限制沟于,這個(gè)之前的文章寫過很多次了,32位機(jī)器最多只能使用2GB的內(nèi)存空間植康,64位機(jī)器可以認(rèn)為沒有上限
然后我們來看一下MemCache的原理旷太,MemCache最重要的莫不是內(nèi)存分配的內(nèi)容了,MemCache采用的內(nèi)存分配方式是固定空間分配销睁,還是自己畫一張圖說明:
這張圖片里面涉及了slab_class供璧、slab、page榄攀、chunk四個(gè)概念嗜傅,它們之間的關(guān)系是:
1、MemCache將內(nèi)存空間分為一組slab
2檩赢、每個(gè)slab下又有若干個(gè)page,每個(gè)page默認(rèn)是1M违寞,如果一個(gè)slab占用100M內(nèi)存的話贞瞒,那么這個(gè)slab下應(yīng)該有100個(gè)page
3、每個(gè)page里面包含一組chunk趁曼,chunk是真正存放數(shù)據(jù)的地方军浆,同一個(gè)slab里面的chunk的大小是固定的
4、有相同大小chunk的slab被組織在一起挡闰,稱為slab_class
MemCache指令匯總
已知MemCache的某個(gè)節(jié)點(diǎn)乒融,直接telnet過去掰盘,就可以使用各種命令操作MemCache了,下面看下MemCache有哪幾種命令:
命 ? ?令? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??作 ? ?用
get? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 返回Key對(duì)應(yīng)的Value值
add? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?添加一個(gè)Key值赞季,沒有則添加成功并提示STORED愧捕,有則失敗并提示NOT_STORED
set? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?無條件地設(shè)置一個(gè)Key值,沒有就增加申钩,有就覆蓋次绘,操作成功提示STORED
replace? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 按照相應(yīng)的Key值替換數(shù)據(jù),如果Key值不存在則會(huì)操作失敗
stats? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?返回MemCache通用統(tǒng)計(jì)信息(下面有詳細(xì)解讀)
stats? items? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 返回各個(gè)slab中item的數(shù)目和最老的item的年齡(最后一次訪問距離現(xiàn)在的秒數(shù))
stats? slabs? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 返回MemCache運(yùn)行期間創(chuàng)建的每個(gè)slab的信息(下面有詳細(xì)解讀)
version? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 返回當(dāng)前MemCache版本號(hào)
flush_all? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 清空所有鍵值撒遣,但不會(huì)刪除items邮偎,所以此時(shí)MemCache依舊占用內(nèi)存
quit? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?關(guān)閉連接
Memcache與Redis的區(qū)別都有哪些?
1)义黎、存儲(chǔ)方式
Memecache不支持持久化禾进,把數(shù)據(jù)全部存在內(nèi)存之中,斷電后會(huì)掛掉廉涕,數(shù)據(jù)不能超過內(nèi)存大小泻云。
Redis 支持兩種持久化策略:RDB 快照和AOF 日志。
2)火的、數(shù)據(jù)支持類型
Memcache對(duì)數(shù)據(jù)類型支持相對(duì)簡(jiǎn)單壶愤,僅支持字符串類型。
Redis有復(fù)雜的數(shù)據(jù)類型馏鹤,支持五種不同的數(shù)據(jù)類型征椒。
3)、使用底層模型不同
它們之間底層實(shí)現(xiàn)方式以及與客戶端之間通信的應(yīng)用協(xié)議不一樣湃累。
Redis直接自己構(gòu)建了VM機(jī)制 勃救,因?yàn)橐话愕南到y(tǒng)調(diào)用系統(tǒng)函數(shù)的話,會(huì)浪費(fèi)一定的時(shí)間去移動(dòng)和請(qǐng)求治力。
4)蒙秒、value大小
redis最大可以達(dá)到1GB,而memcache只有1MB
5)宵统、Redis支持?jǐn)?shù)據(jù)的備份晕讲,即master-slave模式的數(shù)據(jù)備份。
6)马澈、分布式
Memcached 不支持分布式瓢省,只能通過在客戶端使用一致性哈希來實(shí)現(xiàn)分布式存儲(chǔ),這種方式在存儲(chǔ)和查詢時(shí)都需要先在客戶端計(jì)算一次數(shù)據(jù)所在的節(jié)點(diǎn)痊班。
Redis Cluster 實(shí)現(xiàn)了分布式的支持勤婚。
7)、內(nèi)存管理機(jī)制
在Redis 中涤伐,并不是所有數(shù)據(jù)都一直存儲(chǔ)在內(nèi)存中馒胆,可以將一些很久沒用的 value 交換到磁盤缨称,而 Memcached 的數(shù)據(jù)則會(huì)一直在內(nèi)存中。
Memcached 將內(nèi)存分割成特定長(zhǎng)度的塊來存儲(chǔ)數(shù)據(jù)祝迂,以完全解決內(nèi)存碎片的問題睦尽。但是這種方式會(huì)使得內(nèi)存的利用率不高,例如塊的大小為 128 bytes液兽,只存儲(chǔ) 100 bytes 的數(shù)據(jù)骂删,那么剩下的 28 bytes 就浪費(fèi)掉了。
Redis相比memcache有哪些優(yōu)勢(shì)四啰?
(1) memcache所有的值均是簡(jiǎn)單的字符串宁玫,redis作為其替代者,支持更為豐富的數(shù)據(jù)類型
(2) redis的速度比memcache快很多
(3) redis可以持久化其數(shù)據(jù)
參考書目:《高性能Mysql》柑晒、《Redis 設(shè)計(jì)與實(shí)現(xiàn)》