簡(jiǎn)介
memcached和redis都屬于內(nèi)存(memory)鍵-值(key-value)數(shù)據(jù)庫(kù)素邪,在設(shè)計(jì)和思想上有許多相同之處讯檐,功能和應(yīng)用在很多場(chǎng)合(如分布式緩存服務(wù))也相似。它們都從屬于數(shù)據(jù)庫(kù)解決方案中的nosql家族啥纸,由于兩者都將數(shù)據(jù)存儲(chǔ)在內(nèi)存中简识,自然而然冲秽,它們都是非常理想的緩存實(shí)現(xiàn)方案荆萤。
memcached最初是由Brad Fitzpatrick于2003年開(kāi)發(fā)而成。而redis則由Salvatore Sanfilippo于2009年創(chuàng)建铣卡,它本身也從memcached上吸取借鑒大量寶貴經(jīng)驗(yàn)教訓(xùn)链韭,被稱為“強(qiáng)化版memcached”。確實(shí)煮落,redis在功能多樣性方面要?jiǎng)龠^(guò)memcached敞峭,雖然強(qiáng)大且更具靈活性,但復(fù)雜程度也比memcached更甚蝉仇。
基本架構(gòu)和思想
兩者的架構(gòu)和設(shè)計(jì)思想
memcached
memcached采用客戶端-服務(wù)器(C/S)的架構(gòu)旋讹,客戶端和服務(wù)器端的通訊使用自定義的協(xié)議標(biāo)準(zhǔn),只要滿足協(xié)議格式要求轿衔,客戶端Library可以用任何語(yǔ)言實(shí)現(xiàn)沉迹。
從用戶的角度來(lái)說(shuō),服務(wù)器維護(hù)了一個(gè)鍵-值關(guān)系的數(shù)據(jù)表害驹,服務(wù)器之間相互獨(dú)立鞭呕,互相之間不共享數(shù)據(jù)也不做任何通訊操作。也就是說(shuō)宛官,memcached本身不支持分布式擴(kuò)展葫松⊥吒猓客戶端需要知道所有的服務(wù)器,并自行負(fù)責(zé)管理數(shù)據(jù)在各個(gè)服務(wù)器間的分配腋么。
在服務(wù)器端咕娄,內(nèi)部的數(shù)據(jù)存儲(chǔ),使用基于Slab的內(nèi)存管理方式珊擂,有利于減少內(nèi)存碎片和頻繁分配銷毀內(nèi)存所帶來(lái)的開(kāi)銷圣勒。各個(gè)Slab按需動(dòng)態(tài)分配一個(gè)page的內(nèi)存(和4Kpage的概念不同,這里默認(rèn)page為1M)未玻,page內(nèi)部按照不同slab class的尺寸再劃分為內(nèi)存chunk供服務(wù)器存儲(chǔ)KV鍵值對(duì)使用灾而。
memcached的基本應(yīng)用模型:
redis
redis的基本應(yīng)用模式和上圖memcached的基本相似,不難發(fā)現(xiàn)網(wǎng)上到處都是關(guān)于redis是否可以完全替代memcached使用的問(wèn)題扳剿。
redis內(nèi)部的數(shù)據(jù)結(jié)構(gòu)最終也會(huì)落實(shí)到key-value對(duì)應(yīng)的形式旁趟,不過(guò)從暴露給用戶的數(shù)據(jù)結(jié)構(gòu)來(lái)看,要比memcached豐富庇绽,除了標(biāo)準(zhǔn)的通常意義的鍵值對(duì)锡搜,redis還支持List,Set瞧掺, Hashes耕餐,Sorted Set等數(shù)據(jù)結(jié)構(gòu)。
基本命令
memcached的命令或者說(shuō)通訊協(xié)議非常簡(jiǎn)單辟狈,server所支持的命令基本就是對(duì)特定key的添加肠缔,刪除,替換哼转,原子更新明未,讀取等,具體包括 set, get, add, replace, append, inc/dec 等等壹蔓。
memcached的通訊協(xié)議包括文本格式和二進(jìn)制格式趟妥,用于滿足簡(jiǎn)單網(wǎng)絡(luò)客戶端工具(如telnet)和對(duì)性能要求更高的客戶端的不同需求。
redis的命令在kv(string類型)上提供與memcached類似的基本操作佣蓉,在其它數(shù)據(jù)結(jié)構(gòu)上也支持基本類似的操作(當(dāng)然還有這些數(shù)據(jù)結(jié)構(gòu)所特有的操作披摄,如set的union,list的pop等)而支持更多的數(shù)據(jù)結(jié)構(gòu)勇凭,在一定程度上也就意味著更加廣泛的應(yīng)用場(chǎng)合
除了多種數(shù)據(jù)結(jié)構(gòu)的支持疚膊,redis相比memcached還提供了許多額外的特性,比如subscribe/publish命令虾标,以支持發(fā)布/訂閱模式這樣的通知機(jī)制等等酿联,這些額外的特性同樣有助于拓展它的應(yīng)用場(chǎng)景。
redis的客戶端-服務(wù)器通訊協(xié)議完全采用文本格式(在將來(lái)可能的服務(wù)器間通訊會(huì)采用二進(jìn)制格式)。
事務(wù)
redis通過(guò)multi / watch / exec等命令可以支持事務(wù)的概念贞让,原子性的執(zhí)行一批命令周崭。在2.6以后的版本中由于添加了對(duì)script腳本的支持,而腳本固有的是以transaction事務(wù)的方式執(zhí)行的喳张,并且更加易于使用续镇,所以不排除將來(lái)取消multi等命令接口的可能性。
memcached的應(yīng)用模式中销部,除了increment/decrement這樣的原子操作命令摸航,不存在對(duì)事務(wù)的支持。
數(shù)據(jù)備份舅桩,有效性酱虎,持久化等
memcached不保證存儲(chǔ)的數(shù)據(jù)的有效性,slab內(nèi)部基于LRU也會(huì)自動(dòng)淘汰舊數(shù)據(jù)擂涛,客戶端不能假設(shè)數(shù)據(jù)在服務(wù)器端的當(dāng)前狀態(tài)读串,這應(yīng)該說(shuō)是memcached的feature設(shè)定,用戶不必太多關(guān)心或者自己管理數(shù)據(jù)的淘汰更新工作撒妈,當(dāng)然是否適合你的應(yīng)用恢暖,取決于具體的需求,它也可能成為你需要精確自行控制cache生命周期的一個(gè)障礙狰右。
memcached也不做數(shù)據(jù)的持久化工作杰捂,但是有許多基于memcached協(xié)議的項(xiàng)目實(shí)現(xiàn)了數(shù)據(jù)的持久化,例如memcache DB使用BerkeleyDB進(jìn)行數(shù)據(jù)存儲(chǔ)棋蚌,但本質(zhì)上它已經(jīng)不是一個(gè)cache server嫁佳,而只是一個(gè)兼容memcached的協(xié)議key-valueData Store了
redis可以以master-slave的方式配置服務(wù)器,slave節(jié)點(diǎn)對(duì)數(shù)據(jù)進(jìn)行replica備份谷暮,slave節(jié)點(diǎn)也可以充當(dāng)read only的節(jié)點(diǎn)分擔(dān)數(shù)據(jù)讀取的工作
redis內(nèi)建支持兩種持久化方案蒿往,snapshot快照和AOF增量Log方式】辣福快照顧名思義就是隔一段時(shí)間將完整的數(shù)據(jù)dump下來(lái)存儲(chǔ)在文件中。AOF增量Log則是記錄對(duì)數(shù)據(jù)的修改操作(實(shí)際上記錄的就是每個(gè)對(duì)數(shù)據(jù)產(chǎn)生修改的命令本身)情臭,兩種方案可以并存省撑,也各有優(yōu)缺點(diǎn),具體參見(jiàn)http://redis.io/topics/persistence
以上Redis的數(shù)據(jù)備份持久化方案等俯在,如果不需要竟秫,為了提高性能,也完全可以Disable
性能
memcached
memcached自身并不主動(dòng)定期檢查和標(biāo)記哪些數(shù)據(jù)需要被淘汰跷乐,只有當(dāng)再次讀取相關(guān)數(shù)據(jù)時(shí)才檢查時(shí)間戳肥败,或者當(dāng)內(nèi)存不夠使用需要主動(dòng)淘汰數(shù)據(jù)時(shí)進(jìn)一步檢查L(zhǎng)RU數(shù)據(jù)。
redis
redis為了減少大量小數(shù)據(jù)CMD操作的網(wǎng)絡(luò)通訊時(shí)間開(kāi)銷 RTT (Round Trip Time),支持pipeline和script技術(shù)馒稍。
所謂的pipeline就是支持在一次通訊中皿哨,發(fā)送多個(gè)命令給服務(wù)器批量執(zhí)行,帶來(lái)的代價(jià)是服務(wù)器端需要更多的內(nèi)存來(lái)緩存查詢結(jié)果纽谒。
redis內(nèi)嵌了LUA解析器证膨,可以執(zhí)行l(wèi)ua腳本,腳本可以通過(guò)eval等命令直接執(zhí)行鼓黔,也可以使用script load等方式上傳到服務(wù)器端的script cache中重復(fù)使用央勒。
這兩種方式都可以有效地減少網(wǎng)絡(luò)通訊開(kāi)銷,增加數(shù)據(jù)吞吐率澳化。
對(duì)于KV的操作崔步,memcached和redis都支持multiple的get和set命令(memcached的multiple set命令貌似只在二進(jìn)制的協(xié)議中支持),這同樣有利于性能的提升缎谷。
實(shí)際性能方面井濒,網(wǎng)上有很多測(cè)試比較,給出的結(jié)果各不相同慎陵,這無(wú)疑和各種測(cè)試的測(cè)試用例眼虱,測(cè)試環(huán)境,和測(cè)試時(shí)具體使用的客戶端Library實(shí)現(xiàn)有關(guān)席纽。但是總體看下來(lái)捏悬,比較靠譜的結(jié)論是在kv類操作上,兩者的性能接近润梯,memcached的結(jié)構(gòu)更加簡(jiǎn)單过牙,理論上應(yīng)該會(huì)略微快一些。
集群
memcached的服務(wù)器端互相完全獨(dú)立纺铭,客戶端通常通過(guò)對(duì)鍵值應(yīng)用hash算法決定數(shù)據(jù)的分區(qū)寇钉,為了減少服務(wù)器的增減對(duì)hash結(jié)果的影響,導(dǎo)致大面積的緩存失效舶赔,多數(shù)客戶端實(shí)現(xiàn)了一致性hash算法扫倡。
redis計(jì)劃在服務(wù)器端內(nèi)建對(duì)集群的支持,在此之前竟纳,同樣可以認(rèn)為每個(gè)redis服務(wù)器實(shí)例相互之間是完全獨(dú)立的撵溃,需要依靠客戶端處理分區(qū)算法和可用服務(wù)器列表管理的工作。
redis官方推薦的用于sharding的客戶端程序庫(kù)是Twitter的開(kāi)源項(xiàng)目Twemproxy锥累, Twemproxy同時(shí)支持Memcached和Redis的文本通訊協(xié)議缘挑。
需要注意的是,redis的許多命令在集群環(huán)境下是不能正確運(yùn)行的桶略,例如set的交集语淘,以及跨節(jié)點(diǎn)的事務(wù)操作等等诲宇,因?yàn)槟壳暗膔edis集群設(shè)計(jì),根本目標(biāo)也就是服務(wù)器之間互相匯報(bào)一下存活狀態(tài)惶翻,以及對(duì)數(shù)據(jù)做榮譽(yù)備份平衡負(fù)載等而已姑蓝,本質(zhì)上對(duì)數(shù)據(jù)的跨節(jié)點(diǎn)操作并不提供任何額外支持,所以在數(shù)據(jù)服務(wù)的層面上來(lái)說(shuō)维贺,各個(gè)服務(wù)器依舊是完全獨(dú)立的它掂。
這些操作如果一定要實(shí)現(xiàn),當(dāng)然可以通過(guò)客戶端代碼來(lái)實(shí)現(xiàn)(效率有多高且不說(shuō))溯泣,類似的問(wèn)題memcached集群當(dāng)然也會(huì)遇上虐秋,但是原本memcached就不支持復(fù)雜的操作和數(shù)據(jù)類型,許多運(yùn)算邏輯原本就是由客戶端代碼或應(yīng)用程序自己處理的垃沦。
如何選擇
何時(shí)應(yīng)該使用memcached
相對(duì)memcached而言客给,redis的面世時(shí)間更晚且具備更多功能,因此開(kāi)發(fā)人員通常將其視為默認(rèn)性首選方案肢簿。不過(guò)有兩類特殊場(chǎng)景仍然是memcached的一家天下靶剑。首先就是對(duì)小型靜態(tài)數(shù)據(jù)進(jìn)行緩存處理,最具代表性的例子就是HTML代碼片段池充。memcached的內(nèi)部?jī)?nèi)存管理機(jī)制雖然不像redis的那樣復(fù)雜桩引,但卻更具實(shí)際效率——這是因?yàn)閙emcached在處理元數(shù)據(jù)時(shí)所消耗的內(nèi)存資源相對(duì)更少。作為memcached所支持的惟一一種數(shù)據(jù)類型收夸,字符串非常適合用于保存那些只需要進(jìn)行讀取操作的數(shù)據(jù)坑匠,因?yàn)樽址旧頍o(wú)需進(jìn)行進(jìn)一步處理。
除此之外卧惜,memcached在橫向擴(kuò)展方面也比redis更具優(yōu)勢(shì)厘灼。由于其在設(shè)計(jì)上的思路傾向以及相對(duì)更為簡(jiǎn)單的功能設(shè)置,memcached在實(shí)現(xiàn)擴(kuò)展時(shí)的難度比redis低得多咽瓷。不過(guò)根據(jù)我們了解到的情況设凹,目前已經(jīng)有多種經(jīng)過(guò)測(cè)試且切實(shí)有效的方案能夠?qū)edis擴(kuò)展至多臺(tái)服務(wù)器之上,而其即將發(fā)布的3.0版本(感興趣的朋友可以點(diǎn)擊此處查看其候選版本說(shuō)明)將包含專門針對(duì)橫向擴(kuò)展場(chǎng)景的內(nèi)置集群化機(jī)制茅姜。
何時(shí)應(yīng)該使用redis
除非大家需要考慮某種限定性條件(例如處理傳統(tǒng)應(yīng)用程序)對(duì)于memcached的特殊依賴性闪朱,或者自己的實(shí)際用例屬于前面提到的兩類場(chǎng)景中的一種,否則請(qǐng)直接選擇redis并加以運(yùn)用钻洒。憑借著redis所帶來(lái)的卓越緩存方案奋姿,我們將擁有強(qiáng)大的處理能力——例如對(duì)緩存內(nèi)容及持久性進(jìn)行細(xì)節(jié)調(diào)整的能力——以及出色的整體執(zhí)行效率。
redis幾乎在緩存管理工作中的每一個(gè)側(cè)面都表現(xiàn)出顯而易見(jiàn)的優(yōu)越性航唆。這套緩存方案采用所謂數(shù)據(jù)回收機(jī)制胀蛮,能夠?qū)㈥惻f數(shù)據(jù)從內(nèi)存中刪除以提供新數(shù)據(jù)所必需的緩存空間院刁。memcached的數(shù)據(jù)回收機(jī)制使用的是LRU(即最低近期使用量)算法糯钙,而且往往會(huì)比較武斷地直接刪除掉與新數(shù)據(jù)體系相近的原有內(nèi)容。相比之下,redis允許用戶更為精準(zhǔn)地進(jìn)行細(xì)化控制任岸,利用六種不同回收策略確切提高緩存資源的實(shí)際利用率再榄。redis還采用更為復(fù)雜的內(nèi)存管理與回收對(duì)象備選方案。
redis還能為我們帶來(lái)最大程度的靈活性空間享潜,從而保證管理員在打理緩存對(duì)象時(shí)擁有充裕的施展平臺(tái)困鸥。在這方面,memcached將鍵名限制在250字節(jié)剑按,值也被限制在不超過(guò)1MB疾就,且只適用于普通字符串。相比之下艺蝴,redis則將鍵名與值的最大上限各自設(shè)定為512MB猬腰,且支持二進(jìn)制格式。redis支持六種數(shù)據(jù)類型猜敢,因此能夠更加智能地對(duì)數(shù)據(jù)進(jìn)行緩存處理及操作姑荷,這相當(dāng)于為應(yīng)用程序開(kāi)發(fā)人員敞開(kāi)了一道通往無(wú)盡可能性的大門。
相對(duì)于將對(duì)象保存為序列化字符串缩擂,redis允許開(kāi)發(fā)人員以散列方式將對(duì)象域及值加以保存鼠冕,并利用單一鍵對(duì)其進(jìn)行管理。redis散列機(jī)制的存在保證開(kāi)發(fā)人員無(wú)需經(jīng)歷獲取完整字符串胯盯、反序列化懈费、更新值、對(duì)象重新序列化并在每次值更新后利用其替代緩存內(nèi)完整字符串這一系列復(fù)雜的流程——這也意味著資源消耗量得以降低陨闹、性能表現(xiàn)迎來(lái)顯著提升楞捂。redis所支持的其它數(shù)據(jù)類型,例如lists以及sets——也可被用于實(shí)現(xiàn)更加復(fù)雜的緩存管理模式趋厉。
redis的另一大重要優(yōu)勢(shì)在于寨闹,它所保存的數(shù)據(jù)具備透明化特性,也就是說(shuō)服務(wù)器能夠直接對(duì)這些數(shù)據(jù)進(jìn)行操作君账。redis當(dāng)中提供160多種可用命令繁堡,其中大部分用于實(shí)現(xiàn)數(shù)據(jù)處理操作并通過(guò)服務(wù)器端腳本將邏輯嵌入至數(shù)據(jù)存儲(chǔ)體系當(dāng)中。這些內(nèi)置命令及用戶腳本帶來(lái)了極大的靈活性優(yōu)勢(shì)乡数,足以幫助大家直接在redis內(nèi)部完成數(shù)據(jù)處理任務(wù)——而不必將數(shù)據(jù)在網(wǎng)絡(luò)中的其它專門處理系統(tǒng)之間來(lái)回移動(dòng)椭蹄。
redis還提供可選而且能夠具體調(diào)整的數(shù)據(jù)持久性方案,其設(shè)計(jì)目的在于在發(fā)生規(guī)劃內(nèi)停機(jī)或者計(jì)劃外故障之后對(duì)緩存內(nèi)容進(jìn)行重新引導(dǎo)净赴。雖然我們更傾向于強(qiáng)調(diào)緩存內(nèi)數(shù)據(jù)的易失性與暫時(shí)性绳矩,但將數(shù)據(jù)在磁盤中加以持久保存在某些緩存場(chǎng)景當(dāng)中仍然極具現(xiàn)實(shí)意義。這種機(jī)制能夠在設(shè)備重啟之后快速將保存在磁盤上的數(shù)據(jù)重新載入至緩存當(dāng)中玖翅,從而大大縮短緩存預(yù)熱周期并根據(jù)主數(shù)據(jù)存儲(chǔ)內(nèi)容對(duì)當(dāng)前緩存內(nèi)容進(jìn)行重新評(píng)估翼馆。
最后但也同樣重要的一點(diǎn)是割以,redis能夠提供復(fù)制功能。復(fù)制功能旨在幫助緩存體系實(shí)現(xiàn)高可用性配置方案应媚,從而在遭遇故障的情況下繼續(xù)為應(yīng)用程序提供不間斷的緩存服務(wù)严沥。很明顯,一套成熟的緩存方案應(yīng)該能夠在應(yīng)用程序發(fā)生故障時(shí)略微甚至完全不給用戶體驗(yàn)或者應(yīng)用程序性能表現(xiàn)帶來(lái)任何影響中姜,而這種對(duì)緩存內(nèi)容及服務(wù)可用性的有力保障在大多數(shù)情況下也成為緩存解決方案的一大主要優(yōu)勢(shì)消玄。
小結(jié)
redis的作者Salvatore Sanfilippo曾經(jīng)對(duì)這兩種基于內(nèi)存的數(shù)據(jù)存儲(chǔ)系統(tǒng)進(jìn)行過(guò)比較,總體來(lái)看還是比較客觀的丢胚,現(xiàn)總結(jié)如下:
- 性能對(duì)比:由于redis只使用單核翩瓜,而memcached可以使用多核,所以平均每一個(gè)核上redis在存儲(chǔ)小數(shù)據(jù)時(shí)比memcached性能更高携龟。而在100k以上的數(shù)據(jù)中奥溺,memcached性能要高于redis,雖然redis最近也在存儲(chǔ)大數(shù)據(jù)的性能上進(jìn)行優(yōu)化骨宠,但是比起memcached浮定,還是稍有遜色。
- 內(nèi)存使用效率對(duì)比:使用簡(jiǎn)單的key-value存儲(chǔ)的話层亿,memcached的內(nèi)存利用率更高桦卒,而如果redis采用hash結(jié)構(gòu)來(lái)做key-value存儲(chǔ),由于其組合式的壓縮匿又,其內(nèi)存利用率會(huì)高于memcached方灾。另外,memcached使用預(yù)分配的內(nèi)存池的方式碌更,帶來(lái)一定程度的空間浪費(fèi) 并且在內(nèi)存仍然有很大空間時(shí)裕偿,新的數(shù)據(jù)也可能會(huì)被剔除,而redis使用現(xiàn)場(chǎng)申請(qǐng)內(nèi)存的方式來(lái)存儲(chǔ)數(shù)據(jù)痛单,不會(huì)剔除任何非臨時(shí)數(shù)據(jù) redis更適合作為存儲(chǔ)而不是cache嘿棘。
- redis支持服務(wù)器端的數(shù)據(jù)操作:redis相比memcached來(lái)說(shuō),擁有更多的數(shù)據(jù)結(jié)構(gòu)和并支持更豐富的數(shù)據(jù)操作旭绒,通常在memcached里鸟妙,你需要將數(shù)據(jù)拿到客戶端來(lái)進(jìn)行類似的修改再set回去。這大大增加了網(wǎng)絡(luò)IO的次數(shù)和數(shù)據(jù)體積挥吵。在redis中重父,這些復(fù)雜的操作通常和一般的GET/SET一樣高效。所以忽匈,如果需要緩存能夠支持更復(fù)雜的結(jié)構(gòu)和操作房午,那么redis會(huì)是不錯(cuò)的選擇。
另外丹允,貼一些前輩們使用redis的經(jīng)驗(yàn)和教訓(xùn):
- 要進(jìn)行master-slave配置郭厌,出現(xiàn)服務(wù)故障時(shí)可以支持切換嗦锐。
- 在master側(cè)禁用數(shù)據(jù)持久化,只需在slave上配置數(shù)據(jù)持久化沪曙。
- 物理內(nèi)存+虛擬內(nèi)存不足,這個(gè)時(shí)候dump一直死著萎羔,時(shí)間久了機(jī)器掛掉液走。這個(gè)情況就是災(zāi)難。
- 當(dāng)redis物理內(nèi)存使用超過(guò)內(nèi)存總?cè)萘康?/5時(shí)就會(huì)開(kāi)始比較危險(xiǎn)了贾陷,就開(kāi)始做swap,內(nèi)存碎片大缘眶。
- 當(dāng)達(dá)到最大內(nèi)存時(shí),會(huì)清空帶有過(guò)期時(shí)間的key髓废,即使key未到過(guò)期時(shí)間巷懈。
- redis與DB同步寫的問(wèn)題,先寫DB慌洪,后寫redis顶燕,因?yàn)閷憙?nèi)存基本上沒(méi)有問(wèn)題。