韓 兵,李晶晶,方英蘭
(1.北方工業(yè)大學(xué) 計算機學(xué)院,北京 100144; 2.大規(guī)模流數(shù)據(jù)集成與分析技術(shù)北京市重點實驗室,北京 100144)
幾乎所有的Web應(yīng)用都離不開數(shù)據(jù)的支撐,因此在數(shù)據(jù)的訪問過程中,性能成為人們關(guān)注的焦點。隨著互聯(lián)網(wǎng)及其相關(guān)技術(shù)的飛速發(fā)展,網(wǎng)絡(luò)用戶不斷增多,網(wǎng)站的訪問數(shù)量也急劇增加,使Web系統(tǒng)的訪問承受的壓力越來越大,隨之而來的就是系統(tǒng)性能下降,查詢速度降低。大量Web應(yīng)用系統(tǒng)分析表明,在訪問過程中大量結(jié)果集從數(shù)據(jù)庫傳輸?shù)綉?yīng)用程序,是查詢速度滯后的主要原因。因此,如何減少用戶的等待時間,使用戶能夠有效快速地訪問數(shù)據(jù),成為當(dāng)前Web應(yīng)用技術(shù)研究的重點和熱點。
眾所周知,恰當(dāng)?shù)木彺婵梢钥s短數(shù)據(jù)傳輸?shù)木嚯x,縮短系統(tǒng)響應(yīng)時間,提高數(shù)據(jù)利用率和檢索效率。對于應(yīng)用服務(wù)器端的緩存機制,當(dāng)前的做法大多是將上次訪問過的數(shù)據(jù)保存起來,并沒有區(qū)分要緩存的數(shù)據(jù)是否為熱點數(shù)據(jù)[1];當(dāng)需要進行緩存置換時,通常根據(jù)緩存對象的訪問頻率或者最近訪問時間間隔來進行置換,考慮的因素較為單一。例如,EhCache是一種具有代表性的進程內(nèi)緩存框架[2-3],通過手工配置來決定緩存哪些數(shù)據(jù),并提供了三種緩存置換策略:FIFO、LRU、LFU用于緩存管理。但是,該緩存框架并不能自主識別哪些是需要緩存的熱點數(shù)據(jù),緩存置換的方式只能選擇以上一種,不能充分利用現(xiàn)有的緩存資源。
在Web應(yīng)用系統(tǒng)開發(fā)中,合理有效地設(shè)計和使用緩存是提高系統(tǒng)性能的一個重要方式。在數(shù)據(jù)庫管理方面,MySQL數(shù)據(jù)庫使用了query cache[4-5]進行數(shù)據(jù)緩存,以SQL語句為依據(jù)來存儲上一次查詢后的結(jié)果,從而提高數(shù)據(jù)庫的查詢效率,減少大量的磁盤I/O操作和CPU運算。在Oracle數(shù)據(jù)庫中也有類似實現(xiàn),其名稱為buffer cache[6],與MySQL緩存不同的是,它可以通過加入Hint標識符讓用戶自主選擇所要緩存的數(shù)據(jù)。但是這兩種方式都是利用了數(shù)據(jù)庫系統(tǒng)自身的機制,雖然可以提高數(shù)據(jù)庫系統(tǒng)的性能,減少Web系統(tǒng)等待時間,但是在系統(tǒng)中卻無法減少連接數(shù)據(jù)庫的時間開銷,而創(chuàng)建數(shù)據(jù)庫連接和關(guān)閉連接又是一個非常耗時的活動,所以,不僅會使系統(tǒng)的響應(yīng)速度下降,而且會消耗大量不必要的系統(tǒng)資源。
JDBC(Java database connectivity,Java數(shù)據(jù)庫連接)是一個用于連接并操作數(shù)據(jù)庫的規(guī)范[7],主要功能是為多種關(guān)系型數(shù)據(jù)庫提供統(tǒng)一訪問,自身并沒有提供數(shù)據(jù)緩存功能。目前在JDBC的基礎(chǔ)上構(gòu)建了許多更高級的實現(xiàn),比較常見的有Hibernate、Mybatis、Spring JDBC等Web框架。使用框架可以降低系統(tǒng)開發(fā)的復(fù)雜度,減少程序中大量重復(fù)的代碼。其中,Hibernate框架和Mybatis框架都引入了緩存機制[2,8],通過提供一級緩存和二級緩存來提高數(shù)據(jù)的查找效率。一級緩存又稱session級緩存,只能被當(dāng)前事務(wù)訪問,其生命周期對應(yīng)于事務(wù)的生命周期,當(dāng)事務(wù)結(jié)束時,緩存的生命周期也就結(jié)束了。一級緩存是針對單次操作而設(shè)計的,生命周期較短,當(dāng)單次數(shù)據(jù)操作完畢后,下一次請求的數(shù)據(jù)將無法使用上一次緩存的數(shù)據(jù),因此對系統(tǒng)性能的改善是有限的。二級緩存(SessionFactory)是一個可配置的插件,需要通過外部技術(shù)實現(xiàn),存儲的介質(zhì)可以是內(nèi)存或者硬盤。但是此功能需要用戶手工配置,配置信息影響著緩存自身的性能,因此對程序員的要求較高。綜上所述,傳統(tǒng)的數(shù)據(jù)庫緩存、Web框架提供的一級緩存和二級緩存并不能有效地實現(xiàn)緩存中數(shù)據(jù)的共享并減少冗余。且一些早期的Web系統(tǒng)的持久層并沒有使用任何框架[9],而是直接使用JDBC進行編寫,版本遷移比較困難。
對于緩存數(shù)據(jù)的管理,文獻[10]提出了一種基于Chunk Folding的自適應(yīng)多租戶緩存管理機制,依據(jù)用戶當(dāng)前訪問模式,動態(tài)生成候選緩存單元集,并通過貪婪算法選取緩存單元集。文獻[11]根據(jù)用戶訪問頻率建立對應(yīng)具有生命周期的緩存項,維護緩存的一致性。文獻[12]提出了內(nèi)容分發(fā)網(wǎng)絡(luò)緩存替換策略,根據(jù)用戶訪問特性建立相應(yīng)的代價公式,并作為計算Web內(nèi)容價值的要素。
對于持久層采用原生JDBC編寫的Web應(yīng)用系統(tǒng),JDBC作為與數(shù)據(jù)庫進行通信的唯一通道,如果能在獲取數(shù)據(jù)返回給用戶的同時,通過分析其是否是熱點數(shù)據(jù),將熱點數(shù)據(jù)緩存起來,下次發(fā)送相同請求時,則可以直接從緩存中獲取數(shù)據(jù),無需重連數(shù)據(jù)庫。這樣對數(shù)據(jù)庫的大部分查詢就可以轉(zhuǎn)化為對查詢結(jié)果的直接獲取,能夠減少用戶等待時間,提高系統(tǒng)性能。
JDBC是Web應(yīng)用與數(shù)據(jù)庫交互的橋梁,是標準的Java API,提供與數(shù)據(jù)庫進行交互的通道。通過使用JDBC,編程人員可以向不同關(guān)系型數(shù)據(jù)庫發(fā)送SQL命令,實現(xiàn)對數(shù)據(jù)庫中數(shù)據(jù)的增、刪、改、查等操作。
JDBC數(shù)據(jù)管理的模型如圖1所示,模型主要包括三部分:頂層是應(yīng)用層;中間層是應(yīng)用服務(wù)器,緩存管理器(cache manager,CM)是該層的核心與重點;底層是數(shù)據(jù)庫。

圖1 JDBC數(shù)據(jù)管理模型
應(yīng)用層負責(zé)接收用戶請求并發(fā)送至Web應(yīng)用服務(wù)器,并顯示數(shù)據(jù)給用戶,響應(yīng)時間決定著用戶體驗的好壞。
Web應(yīng)用服務(wù)器負責(zé)應(yīng)用層與數(shù)據(jù)庫之間的數(shù)據(jù)處理及交互。在該層中增加緩存數(shù)據(jù)管理的功能,包括三部分內(nèi)容:緩存匹配、緩存置換和一致性維護。當(dāng)接收到用戶的查詢請求時,首先將查詢的SQL語句進行規(guī)范化處理(去掉多余的空格和換行,并把所有的小寫字母變成大寫字母)并交由CM,如果能夠在緩存中匹配到相應(yīng)的目標數(shù)據(jù),則將匹配結(jié)果返回給應(yīng)用層,否則將通過JDBC連接數(shù)據(jù)庫,并發(fā)送查詢命令進行查詢,從而獲取用戶請求的數(shù)據(jù)。在將查詢結(jié)果返回給應(yīng)用層的同時判斷其是否是熱點數(shù)據(jù),如果是則將其保存在緩存空間中。對于緩存空間中的緩存置換[13],采用基于價值函數(shù)的緩存置換算法。當(dāng)緩存中的數(shù)據(jù)被修改時,還需要對緩存數(shù)據(jù)的一致性[14]進行維護。其中,緩存置換是文中研究的重點內(nèi)容。數(shù)據(jù)庫則用于存儲數(shù)據(jù),對數(shù)據(jù)進行處理。
由于服務(wù)器端內(nèi)存資源有限,分配給查詢結(jié)果集的緩存空間大小也是有限的。當(dāng)緩存中緩存數(shù)據(jù)達到容量極限后,為了滿足新緩存需求,需要進行緩存置換。一個好的緩存置換算法是提升緩存性能的關(guān)鍵。
在Web應(yīng)用中,當(dāng)用戶希望查看自己關(guān)心的內(nèi)容時,Web應(yīng)用往往需要向數(shù)據(jù)庫發(fā)送查詢請求,通過JDBC連接數(shù)據(jù)庫來獲取相應(yīng)的結(jié)果集,最終將結(jié)果呈現(xiàn)給用戶。根據(jù)時間局部性原理,如果用戶訪問了一段數(shù)據(jù),那么在近期再次訪問它的可能性較大。在一個Web應(yīng)用中,用戶存在多次查看曾經(jīng)訪問過的頁面的可能,這時需要重新向數(shù)據(jù)庫發(fā)起查詢請求獲得相應(yīng)的查詢結(jié)果。因此,文中將一個查詢請求對應(yīng)的結(jié)果集作為一個緩存項,JDBC管理的緩存則是由不同的緩存項組成的集合,記為CS,每個緩存項記為C,其緩存價值記為V。
為了方便描述,將緩存項的邏輯結(jié)構(gòu)表示為如下的六元組:
C=(ID,R,V,L,S,A)
元組中每個屬性的含義如下:
(1)ID是緩存項唯一標識,由于緩存項是由查詢SQL語句獲得的,故ID表示經(jīng)過規(guī)范化的SQL語句。
(2)R是緩存項的內(nèi)容,每一個緩存項都包含若干條相似的記錄。
(3)V是緩存項的價值,當(dāng)需要進行緩存置換時,根據(jù)這個屬性選取價值小的緩存項進行置換。
(4)L是緩存對象在緩存中的位置。每當(dāng)有新的緩存項加入緩存時,總是將其放在緩存中第一個位置。
(5)S是緩存項占用存儲空間的大小,該因素會影響緩存置換。
(6)A是緩存項從創(chuàng)建開始到需要進行緩存置換時的訪問次數(shù),根據(jù)此屬性可以計算其占總訪問次數(shù)的比重,從而計算其相對訪問頻率。
當(dāng)用戶通過Web應(yīng)用向Web服務(wù)器發(fā)送查詢請求時,首先根據(jù)請求的SQL語句在JDBC管理的緩存中進行匹配查找,如果能夠匹配到,即緩存命中,則將即匹配到的結(jié)果返回給用戶,并更改該緩存項在緩存中的位置和及其緩存價值。如果不能匹配到,則需要通過JDBC從數(shù)據(jù)庫中獲取查詢的結(jié)果集,在將結(jié)果集返回給用戶的同時將其加入緩存,如果需要進行緩存置換,則使用基于價值函數(shù)的緩存置換算法,將緩存空間中價值較小的緩存項移出緩存。緩存置換原理如圖2所示。

圖2 緩存置換原理
文中采用的緩存置換算法對經(jīng)典置換算法(LRU)[15]作了改進,將新加入的緩存項和最新命中的緩存項都移到緩存空間的第一個位置,緩存項越靠前,表示在與當(dāng)前時間間隔越短的時間內(nèi),該緩存項被訪問過。故采用緩存項的位置來計算其LRU價值[16],計算方式如下:
(1)
其中,N表示緩存空間中緩存項的總個數(shù);L表示緩存項在緩存空間中的位置;VLRU(LC)表示在位置LC上緩存項的LRU價值。
通過式1可以看出,每個緩存項都存在一個LRU價值,位置值越小,LRU價值越大,值域在0和1之間。
同時將緩存項的訪問頻率所占比重以及緩存項占用的緩存空間大小等因素都考慮在內(nèi),故緩存項的緩存價值可以通過式2計算:
(2)
其中,VC表示緩存項的緩存價值;Asum表示總體的訪問次數(shù);μ1和μ2表示兩個可調(diào)節(jié)的參數(shù),μ1表示緩存項的訪問頻率對緩存價值的影響權(quán)重,μ2表示LRU價值對緩存價值的影響權(quán)重;N和L的定義參見式1;SC表示緩存項占用緩存空間的大小。
基于價值函數(shù)的緩存置換算法流程如圖3所示。

圖3 基于價值函數(shù)的緩存置換算法流程
當(dāng)查詢請求到來時,如果能夠在緩存空間中找到要請求的數(shù)據(jù),則將該緩存項移動到緩存中的第一個位置,并將位于其之前的緩存項后移一個位置,表示該緩存項具有較小的訪問時間間隔,然后根據(jù)式2重新計算每個緩存項的價值;否則,Web應(yīng)用通過JDBC從數(shù)據(jù)庫中獲取查詢結(jié)果,在將其加入緩存空間時判斷是否達到緩存置換的閾值,如果是,則根據(jù)緩存項的價值從小到大排序,將價值最小的一個或幾個緩存項移出緩存,將新的緩存項放入到緩存中的第一個位置;如果未達到緩存置換的閾值,則直接將新的緩存項放到第一個位置,然后將查詢結(jié)果返回給用戶。
記觸發(fā)緩存置換的閾值為Y,待加入的緩存項為c,則算法描述如下:
開始:
1.if(sizeof(CS) 2.//緩存空間未滿,可以直接插入 3.將每個緩存項往后移動一個位置 4.將c放入到緩存集合CS的第一個位置,且Ac+1 5.根據(jù)式2計算每個緩存項的緩存價值 6.} 7.else{ 8.將CS中每個緩存項按照價值V進行排序 9.do{ 10.將V最大的緩存項移出緩存 11.}while(sizeof(CS) 12.將剩下的每個緩存項往后移動一個位置 13.將c放入到緩存集合CS的第一個位置,且Ac+1 14.重新計算每個緩存項的緩存價值 15.} 結(jié)束 文中研究的是基于JDBC數(shù)據(jù)緩存的管理,通過對緩存項的分析,得出了基于價值函數(shù)的緩存置換算法。為了驗證算法的有效性,本節(jié)設(shè)計了一個模擬實驗。實驗環(huán)境中使用的Web應(yīng)用服務(wù)器為Tomcat服務(wù)器,配置為CPU @3.40 GHz,操作系統(tǒng)為Windows 7,內(nèi)存為8 GB,數(shù)據(jù)庫使用MySQL5.5版本。 在Web應(yīng)用系統(tǒng)中,數(shù)據(jù)的訪問特征服從Zipf[17]分布,即系統(tǒng)中百分之二十的內(nèi)容,占據(jù)了百分之八十的訪問量。因此,實驗中使用一段MATLAB代碼生成符合Zipf分布的10 000個隨機數(shù),范圍是1至500,分別對應(yīng)系統(tǒng)中500個查詢請求,其中有80%的數(shù)字是1到500這500個數(shù)字中的20%的數(shù)字,這符合用戶的訪問規(guī)律。式2中分別取μ1等于0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9,對應(yīng)的μ2等于0.9,0.8,0.7,0.6,0.5,0.4,0.3,0.2,0.1,對于每一對參數(shù),使用基于價值函數(shù)的緩存置換算法計算命中率,實驗結(jié)果如圖4所示。可以看出,當(dāng)μ1=0.8,μ2=0.2時,緩存命中率最高。 根據(jù)上述實驗得出的結(jié)論,取μ1為0.8、μ2為0.2繼續(xù)進行實驗。初始化緩存空間的大小為50 M,用10 000個隨機數(shù)代表用戶的訪問序列,依次發(fā)送1 000,2 000,3 000,4 000,5 000,6 000,7 000,8 000,9 000,10 000個訪問序列,通過代碼讀取訪問序列對應(yīng)的查詢請求序列,向Web服務(wù)器發(fā)送相應(yīng)的SQL命令,如果緩存命中則該SQL語句對應(yīng)的緩存項的命中次數(shù)加1,如果未命中則需要通過數(shù)據(jù)庫獲取結(jié)果,將其保存在緩存空間中。對于傳統(tǒng)的LRU緩存置換算法,當(dāng)緩存空間不足時替換出訪問時間最早的緩存項。而文中提出的基于價值函數(shù)的置換算法則綜合考慮了緩存項的訪問頻率、訪問時間間隔和緩存項的大小,選取價值最小的緩存項進行置換。對于每一個訪問序列,分別應(yīng)用LRU置換算法和基于價值函數(shù)的置換算法,統(tǒng)計總的命中次數(shù),計算兩種算法的命中率,對比結(jié)果如圖5所示。 圖5 緩存置換算法命中率對比曲線 從圖5可以看出,隨著訪問數(shù)量增多,基于價值函數(shù)的緩存置換算法的緩存命中率高于傳統(tǒng)的LRU算法,所以使用基于價值函數(shù)的緩存置換算法可以提高JDBC緩存的性能。 實驗中對于每一次發(fā)送的查詢請求,分別記錄了使用緩存和不使用緩存時Web服務(wù)器的響應(yīng)時間,結(jié)果如圖6所示。從圖中可以明顯看出,當(dāng)使用緩存時,服務(wù)器的響應(yīng)時間明顯高于不使用緩存時的響應(yīng)時間。這是因為當(dāng)使用緩存時,不需要連接數(shù)據(jù)庫就能直接從緩存中獲取所需要的數(shù)據(jù),減少了連接數(shù)據(jù)庫和在數(shù)據(jù)庫服務(wù)器中進行查詢的時間,提高了數(shù)據(jù)訪問的效率。因此,文中設(shè)計的基于JDBC數(shù)據(jù)管理的模型對于JDBC緩存是非常必要的。 圖6 服務(wù)器響應(yīng)時間對比曲線 通過對JDBC中的類和接口進行修改和擴展,設(shè)計了一個基于JDBC數(shù)據(jù)管理的模型,綜合考慮緩存項的訪問頻率、訪問時間間隔和緩存項占用存儲空間的大小等因素,提出了一種基于價值函數(shù)的緩存置換算法。通過設(shè)計模擬實驗,從緩存的命中率、服務(wù)器的響應(yīng)時間兩方面進行分析,相比于沒有緩存功能的JDBC,該模型能夠獲得較高的訪問效率,可以有效彌補使用原生JDBC進行開發(fā)的Web系統(tǒng)的訪問效率問題,提升系統(tǒng)性能。 另外,由于改進后的JDBC針對的是全表查詢的數(shù)據(jù),而實際應(yīng)用中往往是多表聯(lián)合查詢,因此如何細化緩存粒度,找出更高效的緩存管理方式有待進一步的研究。4 實驗設(shè)計與驗證
4.1 實驗數(shù)據(jù)
4.2 緩存命中率對比分析
4.3 響應(yīng)時間對比分析
5 結(jié)束語