馬榮彥
(中央宣傳部電影數(shù)字節(jié)目管理中心,北京 100866)
近年來,隨著計(jì)算機(jī)互聯(lián)網(wǎng)技術(shù)的發(fā)展,大數(shù)據(jù)的存取以及網(wǎng)站的快速響應(yīng)成為現(xiàn)在web應(yīng)用發(fā)展所面臨的一個(gè)巨大的挑戰(zhàn)。農(nóng)村數(shù)字電影公共服務(wù)平臺基于影片信息庫中的影片向監(jiān)管部門、院線、發(fā)行方等用戶提供了流動(dòng)放映業(yè)務(wù)的信息、制作、分發(fā)、管理等技術(shù)監(jiān)管服務(wù),影片基礎(chǔ)信息庫中引入了許多市場上的影片新片,這是整個(gè)平臺賴以生存的基礎(chǔ),各種角色的用戶登陸平臺都需要瀏覽查詢影片的基本信息,這對平臺的檢索展現(xiàn)速度有了更高的要求,同時(shí)也對數(shù)據(jù)庫的性能方面提出了更高的要求。為了保證系統(tǒng)運(yùn)行性能的冗余,查詢性能是系統(tǒng)整體優(yōu)化的關(guān)鍵,因此引入memcached分布式緩存來減輕數(shù)據(jù)庫壓力,它通過將從數(shù)據(jù)庫中得到的查詢結(jié)果緩存起來,來減少系統(tǒng)與DB 的訪問連接頻次,這樣系統(tǒng)讀取數(shù)據(jù)的時(shí)候如果命中就會從緩存中獲取數(shù)據(jù),來提高系統(tǒng)頁面的展示速度,增強(qiáng)用戶體驗(yàn)。
大部分應(yīng)用系統(tǒng)都將數(shù)據(jù)存儲到數(shù)據(jù)庫管理系統(tǒng)RDMS中,應(yīng)用服務(wù)從數(shù)據(jù)庫中讀取相應(yīng)的數(shù)據(jù)并進(jìn)行頁面展示。但有時(shí)會由于數(shù)據(jù)庫管理系統(tǒng)響應(yīng)變慢、頁面數(shù)據(jù)或者圖片延遲展示等問題影響用戶的訪問體驗(yàn),這是由于系統(tǒng)在一段時(shí)間內(nèi)訪問量的增長或者對頁面中通用展示數(shù)據(jù)集中訪問而導(dǎo)致的。
memcached經(jīng)常被用來在動(dòng)態(tài)互聯(lián)網(wǎng)服務(wù)中緩解數(shù)據(jù)庫的負(fù)載壓力。它的主要原理是通過緩存第一次數(shù)據(jù)庫查詢結(jié)果,通過算法提高數(shù)據(jù)命中率,來減少和數(shù)據(jù)庫之間的連接訪問次數(shù)。memcached基于存儲鍵/值對的hashmap,memcached 在啟動(dòng)時(shí)增加-d參數(shù)將其啟動(dòng)為后臺運(yùn)行進(jìn)程即為守護(hù)進(jìn)程,盡管它的守護(hù)進(jìn)程是C 語言編寫的,但是客戶端程序只要支持memcached協(xié)議,不管使用何種語言都可以與之進(jìn)行通信。
memcached 進(jìn)程運(yùn)行之后,會提前申請一塊較大的內(nèi)存空間由自己進(jìn)行管理,用完之后再去申請,不是每次使用時(shí)都去向操作系統(tǒng)申請。所以如果分配足夠大的內(nèi)存空間給memcached的時(shí)候,基本上memcached的時(shí)間消耗就只剩下網(wǎng)絡(luò)連接的時(shí)間了。
圖1 memcached使用方式
memcached作為一個(gè)高效的分布式緩存,它是由自己向服務(wù)器申請一塊內(nèi)存,對存儲的Hash-Table內(nèi)容進(jìn)行有效管理。服務(wù)器的內(nèi)存使用一般僅限于自身使用,不能進(jìn)行共享,而memcached的出現(xiàn)解決了此問題,允許多個(gè)用戶同時(shí)進(jìn)行訪問,并且同時(shí)使用,而且也不會發(fā)生在與數(shù)據(jù)庫進(jìn)行連接訪問的時(shí)候因磁盤讀寫消耗資源較多導(dǎo)致進(jìn)程阻塞的情況。它主要有以下幾大特點(diǎn)。
(1)基于文本行的通信協(xié)議:memcached的內(nèi)容管理采用的是簡單的、便于操作的、基于文本行的協(xié)議,即使通過遠(yuǎn)程登錄也能對緩存內(nèi)容數(shù)據(jù)進(jìn)行讀取。
(2)基于libevent庫的處理:libevent是一個(gè)事件通知程序庫,它將Linux的許多事件處理功能封裝起來進(jìn)行統(tǒng)一調(diào)用,而且在多線程方面有很好的性能。memcached基于此庫可以高效地運(yùn)行在多種類型的操作系統(tǒng)OS上。
(3)預(yù)申請內(nèi)存的方式:memcached會提前申請較大的內(nèi)存空間供自己使用,常用的數(shù)據(jù)都會被存放在申請的空間中,并且會基于某些算法(例如:LRU 即最近最少使用算法)自動(dòng)移除不經(jīng)常使用的緩存數(shù)據(jù),騰出空間給需要緩存的數(shù)據(jù),并且它也會隨著memcached或者操作系統(tǒng)的重啟全部消失。
(4)獨(dú)立的分布式緩存:memcached的緩存是一種通過客戶端程序來實(shí)現(xiàn)的分布式緩存,它們獨(dú)立工作,多個(gè)memcached 不會互相通信來共享信息,亦不會相互干擾,解決了共享內(nèi)存只能單機(jī)應(yīng)用的局限。
(5)支持多種語言的客戶端:許多語言都實(shí)現(xiàn)了memcached的客戶端,僅memcached網(wǎng)站上列出的語言就有Perl、PHP、Python、Ruby、C#、C/C++、java等。
memcached的內(nèi)存緩存用于在系統(tǒng)中提升系統(tǒng)的響應(yīng)速度。它的主要應(yīng)用場景如下:
(1)由于memcached緩存是基于分布式的,相對來說比較適合分布式服務(wù)系統(tǒng)。
(2)獨(dú)立于應(yīng)用:web應(yīng)用系統(tǒng)響應(yīng)慢的重要瓶頸是數(shù)據(jù)庫的高并發(fā),和其他的緩存機(jī)制例如java的Hibernate緩存機(jī)制比較,Hibernate是和應(yīng)用程序本身的耦合性比較高,不像memcached是基于分布式的、獨(dú)立于應(yīng)用系統(tǒng)的。
(3)不同服務(wù)系統(tǒng)間信息互通:兩個(gè)不同的應(yīng)用服務(wù)系統(tǒng)信息需要同步,這時(shí)候就可以使用memcached了,其中一個(gè)系統(tǒng)將需要共享的信息進(jìn)行memcached緩存,另一個(gè)系統(tǒng)服務(wù)就可以通過memcached獲得共享的信息,就像獲取本地信息一樣方便。
如果使用memcached后不僅不會帶來任何便利之處,相反還會拖慢整個(gè)系統(tǒng),因?yàn)榫W(wǎng)絡(luò)連接同樣需要消耗資源,那么這時(shí)候就不適合使用memcached。不適合使用memcached的主要業(yè)務(wù):
(1)數(shù)據(jù)對象占用較大空間:由于memcached存儲信息在內(nèi)存中,空間有限因此不適合那些較大數(shù)據(jù)塊的存儲。
(2)應(yīng)用服務(wù)運(yùn)行在內(nèi)存不受控制的服務(wù)器上:memcached需要申請和控制大塊的內(nèi)存供自己調(diào)配,如果memcached管理的內(nèi)存被其它服務(wù)占用或者丟失,memcached的緩存命中率將會大大降低,性能也會隨之下降。
(3)沒有安全策略的應(yīng)用中:如果沒有安全保障,memcached緩存的數(shù)據(jù)就很容易被不適當(dāng)?shù)倪M(jìn)程獲取,memcached本身并未提供任何安全策略以及安全管理機(jī)制,因此需要對服務(wù)本身以及運(yùn)行環(huán)境考慮增強(qiáng)安全策略。
(4)需要存儲的是持久化數(shù)據(jù):由于memcached本身是為緩存而設(shè)計(jì)的服務(wù)器,因此它的數(shù)據(jù)是有時(shí)效的,在緩存數(shù)據(jù)量達(dá)到一定的值后,就會根據(jù)某些特定的算法移除部分緩存數(shù)據(jù),并不適合永久性數(shù)據(jù)的存儲情況。
本地緩存local cache顧名思義就是應(yīng)用服務(wù)器本身的緩存空間,它能夠利用的內(nèi)存容量受到服務(wù)器空閑內(nèi)存空間的限制,memcached則不會,不過local cache不但可以存儲任意的數(shù)據(jù),而且沒有網(wǎng)絡(luò)存取的延遲,在這一點(diǎn)上有其獨(dú)特的優(yōu)勢。
(1)本地緩存數(shù)據(jù)查詢速度更優(yōu):可以把頁面中使用非常頻繁的或者訪問次數(shù)最多的數(shù)據(jù)放在本地緩存中,這樣每次加載數(shù)據(jù)時(shí)就可以實(shí)現(xiàn)查詢的秒級響應(yīng),沒有網(wǎng)絡(luò)延遲。
(2)本地緩存不會自動(dòng)更新失效數(shù)據(jù):在memcached集群中,對key-value的修改刪除會通過某種方式同時(shí)通知所有的客戶端獲取此數(shù)據(jù)的變更。這一點(diǎn)本地緩存方式劣勢明顯,它需要刷新服務(wù)器數(shù)據(jù),效率很慢。
(3)本地緩存受限于本地服務(wù)器的空閑內(nèi)存大小。
由于memcached 之間不進(jìn)行任何數(shù)據(jù)復(fù)制備份,本身也沒有內(nèi)置分布式功能,并且服務(wù)器與服務(wù)器之間沒有任何通信都是相互獨(dú)立的,因此memcached本身是沒有任何策略維持失效轉(zhuǎn)發(fā)的,所以當(dāng)任何服務(wù)器節(jié)點(diǎn)出現(xiàn)故障時(shí),可能會導(dǎo)致獲取不到有效數(shù)據(jù),所以我們可以利用magent 將memcache做成集群方式來避免出現(xiàn)單點(diǎn)故障,實(shí)現(xiàn)多臺memcached緩存服務(wù)器的高效管理。
magent是一款比較常用的memcached代理服務(wù)器,從圖2 可以看到有兩個(gè)magent節(jié)點(diǎn),每個(gè)magent節(jié)點(diǎn)又分別連接memcached節(jié)點(diǎn),magent下memcached有主備的區(qū)別,memcache主節(jié)點(diǎn)可以有多臺機(jī)器,它分散存儲所有緩存的鍵值數(shù)據(jù),備節(jié)點(diǎn)則存儲了一個(gè)完整的鍵值數(shù)據(jù)。magent有效地解決了memcached的不能節(jié)點(diǎn)分布式問題,如果其中一臺緩存服務(wù)器宕掉,系統(tǒng)依然可以繼續(xù)工作,magent依然可以讀取到數(shù)據(jù),這樣數(shù)據(jù)就不會丟失并且可以保證數(shù)據(jù)的完整性。特別需要注意的是,memcached重啟后緩存數(shù)據(jù)會被清空,這時(shí)盡管備份的memcached還有數(shù)據(jù),magent取得的仍可能會是空值,可采用定時(shí)維護(hù)服務(wù)器,來同步恢復(fù)memcached緩存數(shù)據(jù)。
圖2 memcached與magent的混合模型
magent和每個(gè)memcached服務(wù)器之間保持著長連接的連接方式,這樣可以減少創(chuàng)建連接、銷毀連接的資源消耗。它和memcached 一樣,基于libevent的事件程序庫來處理IO 讀寫請求,并且支持memcached的許多通信協(xié)議指令,來實(shí)現(xiàn)系統(tǒng)請求的轉(zhuǎn)發(fā)。
memcached的主要原理是通過在預(yù)先申請到的內(nèi)存中維護(hù)一張hashtable來存儲各種格式的數(shù)據(jù),比如圖片、數(shù)字、文本以及從數(shù)據(jù)庫中查詢到的結(jié)果數(shù)據(jù)等。memcached緩存是存儲了很多 〈key,value〉鍵值對的表,通過兩段哈希算法可以存儲或查詢?nèi)我獾臄?shù)據(jù)??蛻舳顺绦?qū)崿F(xiàn)了memcached的分布式,它把數(shù)據(jù)使用一定的算法存儲在不同的memcached緩存服務(wù)器里,不同服務(wù)器存儲的數(shù)據(jù)不同,當(dāng)用戶需要使用此數(shù)據(jù)時(shí),程序會首先使用內(nèi)置算法計(jì)算出(key)的哈希值即階段一哈希,找到一個(gè)服務(wù)器節(jié)點(diǎn)并將數(shù)據(jù)請求發(fā)送給此節(jié)點(diǎn),此節(jié)點(diǎn)再通過一次哈希即階段二哈希,這時(shí)才查找到最后需要的數(shù)據(jù) (value)。各種語言的客戶端實(shí)現(xiàn)的哈希算法是不同的,因此在緩存服務(wù)器中數(shù)據(jù)的存儲方式也是不盡相同的。
假設(shè)有客戶端client,memcached服務(wù)器A、B、C。應(yīng)用程序要保存〈“key1”,“11”〉,〈“key2”,“22”〉的數(shù)據(jù):client首先根據(jù)某種算法計(jì)算出鍵“key1”的哈希值,假設(shè)選中了服務(wù)器A,然后client會與服務(wù)器A 連接,把數(shù)據(jù)“11”存儲到鍵“key1”的value中去。同樣 〈“key2”,“22”〉也通過哈希算法選擇相應(yīng)的緩存服務(wù)器進(jìn)行數(shù)據(jù)存儲。接下來我們要訪問數(shù)據(jù),獲取時(shí)也要將鍵“key1”的hash值傳遞給函數(shù)庫,然后使用與存儲“key1”時(shí)相同的哈希算法 (哈希算法相同,就可以保證兩次選中同一臺服務(wù)器),計(jì)算出“key1”在服務(wù)器A上,然后發(fā)送get 指令,就可以獲得value 數(shù)據(jù)“11”。只要緩存的數(shù)據(jù)沒有因?yàn)楣收稀⒊瑫r(shí)等某些原因被服務(wù)器移除,就能獲得之前存儲的value,如圖3所示。
圖3 memcached存取數(shù)據(jù)示意圖
如果memcached服務(wù)器數(shù)量比較多,不同的數(shù)據(jù)value就會被分配到不同的服務(wù)器上進(jìn)行存儲,這就實(shí)現(xiàn)了memcached的緩存數(shù)據(jù)分散存儲即分布式的功能,而且即使其中一臺服務(wù)器因故障無法被連接,也不會影響其他服務(wù)器的正常運(yùn)行,因?yàn)樗鼈兌际歉髯元?dú)立處理不會相互干擾,在邏輯層面用戶是感覺不到故障的發(fā)生的,因此被認(rèn)為是正常運(yùn)行的。
在了解memcached 的原理之后,為了驗(yàn)證memcached的有效性,本文采用3000 多部影片基礎(chǔ)信息作為測試對象,比較從數(shù)據(jù)庫中查詢數(shù)據(jù)和從memcached中查詢符合條件的20條數(shù)據(jù)的性能。一部影片的基本信息包括影片名稱、許可證號、出品年代、導(dǎo)演、主演、編劇、英文名稱、制片人、時(shí)長、題材、影片類型、國家/地區(qū)、出品單位、攝制單位、影片簡介等。
在對比測試前做好一些準(zhǔn)備工作,首先安裝memcached服務(wù)器端,要先安裝libevent庫,如果系統(tǒng)已有此庫,可跳過。本次實(shí)驗(yàn)采用的是centos操作系統(tǒng),因此使用yum install memcached進(jìn)行安裝,安裝完成后操作目錄為/usr/bin/memcached,并成功啟動(dòng)該服務(wù)。
本文采用的語言為java,因此采用memcached的java客戶端xmemcached 版本2.0.0 進(jìn)行測試,對于memcached的所有java客戶端之間的對比可以參考下一節(jié)內(nèi)容。表1為單線程的情況下查詢10次的平均值的對比測試結(jié)果。
表1 從數(shù)據(jù)庫中和memcached中查詢數(shù)據(jù)的性能對比結(jié)果
從表1和表2可以看到memcached存取數(shù)據(jù)的時(shí)間明顯比數(shù)據(jù)庫中直接查詢有很大的提升,尤其是線程數(shù)越多越明顯。當(dāng)數(shù)據(jù)庫的連接數(shù)目超過一定的值后會造成數(shù)據(jù)庫的崩潰,因此memcached對于緩解數(shù)據(jù)庫的壓力有很好的幫助。
表2 不同線程數(shù)情況下響應(yīng)時(shí)間結(jié)果比較
盡管系統(tǒng)使用memcached后性能有了很大的提升,但畢竟它只是緩存,這時(shí)如果數(shù)據(jù)發(fā)生變化,我們可以有兩種方式進(jìn)行處理:一是對緩存和數(shù)據(jù)庫的數(shù)據(jù)同時(shí)進(jìn)行更新,二是對緩存的數(shù)據(jù)進(jìn)行直接移除或者刪除,等下次訪問的時(shí)候再進(jìn)行處理,這樣就可以避免讀取到臟數(shù)據(jù),造成系統(tǒng)數(shù)據(jù)展示不正確。因?yàn)閷懖僮骺偸且匦逻M(jìn)行緩存處理,消耗大量的資源占用帶寬,因此緩存是不適合有大量寫和更新操作的數(shù)據(jù)應(yīng)用場景的。
memcachedClient:該客戶端基于傳統(tǒng)的I/O阻塞模型,在高并發(fā)的時(shí)候比較容易報(bào)內(nèi)存溢出的異常。
xmemcached:它的性能和穩(wěn)定性比較高,可以作為首選。它基于Java NIO,和傳統(tǒng)I/O 阻塞模型對比,它的優(yōu)勢比較明顯即效率高、資源耗費(fèi)少,已經(jīng)被越來越多地應(yīng)用到大型應(yīng)用服務(wù)中,成為解決高并發(fā)與大量連接、I/O 處理問題的有效方式。NIO 是一種同步非阻塞的I/O 模型,也是I/O 多路復(fù)用的基礎(chǔ),只需要?jiǎng)?chuàng)建和維護(hù)一個(gè)連接 (當(dāng)然NIO 也可以做池化處理),這樣便省去了線程創(chuàng)建和切換的資源消耗,在并發(fā)量比較多的用戶連接下有著非常突出的表現(xiàn)。
當(dāng)今社會需要提供實(shí)時(shí)的動(dòng)態(tài)頁面和信息,針對數(shù)據(jù)庫高并發(fā)的讀寫需求,如果使用傳統(tǒng)的直連數(shù)據(jù)庫模式,并發(fā)負(fù)載明顯偏高容易造成數(shù)據(jù)庫死鎖或者宕機(jī)。本文對memcached進(jìn)行了簡要的介紹,在3000多部影片基礎(chǔ)信息數(shù)據(jù)的基礎(chǔ)上做了不同的對比,從對比結(jié)果來看,memcached在性能上的優(yōu)異很明顯,并且還可以和magent實(shí)現(xiàn)memcached集群,來解決服務(wù)器節(jié)點(diǎn)單點(diǎn)故障的問題。但是它也有本身的弱點(diǎn),memcached對內(nèi)部存儲的緩存數(shù)據(jù)同等對待,并沒有進(jìn)一步來區(qū)分?jǐn)?shù)據(jù),比如訪問頻次多的數(shù)據(jù)可以更容易被查詢等,因此需要進(jìn)一步的優(yōu)化,可以從命中率、空間利用率、安全性能等許多方面對此進(jìn)行考慮,還需要進(jìn)一步的探索研究。