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