黎子晨
摘 要:企業(yè)開發(fā)中常用到對象關系映射,隨著技術和業(yè)務的發(fā)展,應用系統(tǒng)常需要集群部署來減輕每一臺服務器的壓力。對象關系映射中的緩存技術對于提升性能有較大益處,但在集群環(huán)境中常導致緩存不一致,從而影響業(yè)務處理的正確性。文中設計了緩存同步的設計方案,試圖解決同步問題,為在集群環(huán)境中部署使用ORM緩存技術提供借鑒意義。
關鍵詞:對象關系映射;緩存;集群;同步方案
中圖分類號:TP393 文獻標識碼:A 文章編號:2095-1302(2017)08-00-03
0 引 言
隨著信息化技術的快速發(fā)展,人們依賴于使用軟件解決日常工作中碰到的問題。軟件的規(guī)模隨著技術及業(yè)務的發(fā)展變得越來越龐大和復雜。從過去個人桌面軟件的普及,到現(xiàn)在的分布式軟件架構,軟件系統(tǒng)已經(jīng)和企業(yè)現(xiàn)有的業(yè)務模式緊密結合在一起。對于企業(yè)級系統(tǒng),其最為核心的部分是與業(yè)務緊密關聯(lián)的各類數(shù)據(jù)。在數(shù)據(jù)存儲方面,企業(yè)使用最多的還是關系型數(shù)據(jù)庫系統(tǒng)。關系型數(shù)據(jù)庫從數(shù)學理論發(fā)展而來,和目前主流的軟件開發(fā)方式,即面向?qū)ο蟮能浖_發(fā)方式不匹配。對象關系映射(Object Relational Mapping,ORM)技術的出現(xiàn)從很大程度上解決了兩者的不匹配問題,即以系統(tǒng)開發(fā)程序員的角度來看,數(shù)據(jù)是面向?qū)ο蟮?,而?shù)據(jù)依然存儲于關系型數(shù)據(jù)庫,即數(shù)據(jù)邏輯面向?qū)ο?,物理關系存在于關系模型中。
使用對象關系映射可通過減輕對底層數(shù)據(jù)庫的訪問頻率來提高系統(tǒng)性能。該性能提升功能通過啟用緩存來實現(xiàn),即數(shù)據(jù)的獲取可以通過緩存而非訪問數(shù)據(jù)庫來滿足。緩存通常存在于內(nèi)存中(也可能存在于本地磁盤中),相對于數(shù)據(jù)庫存取數(shù)據(jù)需要訪問磁盤尋找數(shù)據(jù),再將數(shù)據(jù)通過網(wǎng)絡傳輸給請求者,當緩存命中時所帶來的性能提升十分明顯。依據(jù)局部性原理,設置一個合理的緩存將會有一個可觀的緩存命中率,可明顯提升系統(tǒng)的存取性能。
在ORM中開啟緩存存在一個嚴重的問題,即在集群環(huán)境中會產(chǎn)生數(shù)據(jù)不一致的現(xiàn)象。以B/S模式為例:當一個應用只部署在一個Web站點上時,不存在上述問題;但當該應用采取集群方式部署在多個不同的Web站點上時,極有可能出現(xiàn)相同的對象。在不同站點中存在不同緩存項會導致生產(chǎn)環(huán)境中的業(yè)務數(shù)據(jù)出現(xiàn)錯誤。目前主流的ORM框架均不建議采用集群方式來部署,但隨著業(yè)務的發(fā)展,逐漸增多的業(yè)務會對系統(tǒng)產(chǎn)生更大的壓力,采用集群方式部署以減小單臺設備的壓力乃大勢所趨。本文通過提出幾種設計方案來解決集群部署采用ORM技術系統(tǒng)時存在的緩存不一致問題。
1 集群環(huán)境中ORM緩存不一致性分析
在企業(yè)應用系統(tǒng)中,目前使用最為廣泛的架構為瀏覽器-服務器架構,即B/S模式[1]。如圖1所示,在B/S模式中,客戶端使用瀏覽器,通過HTTP協(xié)議訪問部署于應用系統(tǒng)的Web服務器[2]??蛻粝騑eb服務器請求或提交數(shù)據(jù),服務器對客戶的訪問進行處理,將結果返回給客戶,產(chǎn)生的數(shù)據(jù)存儲在數(shù)據(jù)庫中。
當Web服務器中使用了ORM技術后,Web服務器同數(shù)據(jù)庫服務器交換數(shù)據(jù)的方式將有所變化,如圖2所示。Web服務器不再直接通過SQL或存儲過程和數(shù)據(jù)庫服務器進行交互,而是將數(shù)據(jù)對象提交給對象關系映射的框架,該框架再依據(jù)系統(tǒng)操作使用SQL或存儲過程完成數(shù)據(jù)存取,將數(shù)據(jù)存儲于數(shù)據(jù)庫或從中獲取數(shù)據(jù)。使用了ORM框架后,為取數(shù)方式增添了靈活性,易于開發(fā)且具有理想的訪問性能[3]。啟用ORM緩存,當框架從數(shù)據(jù)庫中成功取數(shù)后,獲取的數(shù)據(jù)將會在框架中留有副本,稱之為緩存項,所有的緩存項構成了ORM的數(shù)據(jù)緩存。當進行下一次取數(shù)操作時,應用系統(tǒng)向ORM框架提出取數(shù)請求,ORM框架首先查找緩存項,試圖找出能滿足請求的數(shù)據(jù)對象,并將結果返回給應用系統(tǒng),如果不存在滿足條件的對象則會進行一次數(shù)據(jù)庫訪問請求以獲取所需數(shù)據(jù)。根據(jù)局部性原理,一個適當大小的緩存能明顯提高緩存的命中率,從而減少對數(shù)據(jù)庫的訪問,提升系統(tǒng)性能[4]。
以目前最流行的ORM框架Hibernate為例:在Hibernate中,所有的Session都由同一個SessionFactory實例產(chǎn)生。一級緩存的作用范圍為Session,二級緩存就是Session.Factory級別的緩存。二級緩存可在不同Session對象間共享,它屬于進程范圍的緩存。二級緩存可進行配置和更改,并且可以動態(tài)加載和卸載。當Hibernate發(fā)現(xiàn)緩存中已有該條數(shù)據(jù),便會直接使用,而不必去數(shù)據(jù)庫查詢,當緩存中沒有找到對應項時才會到數(shù)據(jù)庫中查找[5]。
ORM框架使用緩存的方式在單點部署方面不存在任何問題,但在集群環(huán)境下部署會產(chǎn)生數(shù)據(jù)不一致的問題[6]。如圖3所示,在一個網(wǎng)絡中將若干臺客戶端及部署了相同應用系統(tǒng)的兩臺Web服務器作為集群,這兩臺Web服務器會訪問同一個數(shù)據(jù)庫服務器,所有客戶端都可以通過瀏覽器訪問Web服務器1或Web服務器2,并完成相同的工作。若兩臺Web服務器上所受理的業(yè)務為只讀,則不存在上述問題。如果業(yè)務存在讀寫的情況,當不同客戶端通過不同Web服務器讀寫同一數(shù)據(jù)時,則極有可能出現(xiàn)不一致的狀態(tài)。
以銀行轉(zhuǎn)賬業(yè)務為例:存在兩個用戶Tom和Jerry,當Tom收到來自Jerry的匯款后,Tom打算將自己賬戶中所有的金錢全部取出。在數(shù)據(jù)庫中存在關系account(id,money)用于存儲用戶賬戶的金額,其中id作為主鍵用于標識用戶身份,money用于存儲用戶帳戶的金額,Tom的id為1,Jerry的id為2。表1說明了兩個用戶的轉(zhuǎn)賬取款過程。假設Tom賬戶余額為300元,Jerry賬戶余額為400元,Jerry的轉(zhuǎn)賬金額為100元。在關系account中存在兩個元組,分別存儲Tom和Jerry的賬戶金額。正常情況下,時間1時Tom看到自己的余額為300元,時間3的余額為400元,時間4時將400元從自己的賬戶中取出。
表2顯示了在集群環(huán)境中使用ORM緩存后的執(zhí)行過程,Tom取出的金額為300元,但卻不是理想的結果,其問題出現(xiàn)的關鍵在時間序號2和3,即兩個Web服務器中的緩存不一致,沒有客觀反映現(xiàn)實世界的狀態(tài)。解決該問題的關鍵在于如何使數(shù)據(jù)庫和所有啟用ORM緩存的應用服務器對于相同的元組反映一致的狀態(tài)。endprint
2 集群環(huán)境中ORM緩存同步方案設計
為解決在集群環(huán)境中使用ORM帶來的數(shù)據(jù)不一致問題,主要采取以下幾種解決措施:
(1)禁用ORM緩存,使所有數(shù)據(jù)訪問請求都產(chǎn)生一個數(shù)據(jù)庫訪問;
(2)采用某種方式使得對于數(shù)據(jù)庫中記錄的修改都能夠及時反映到所有存儲了該記錄的ORM緩存中。
第一種方案最簡單直接,但會明顯降低系統(tǒng)數(shù)據(jù)存取的性能。對于第二種方案,采用合理的設計方案可以使得在不影響系統(tǒng)性能的前提下使緩存中的數(shù)據(jù)同步,從而得到一致性的狀態(tài)。但在開發(fā)和部署方面會相對復雜,也可能產(chǎn)生一些新的問題。本文將著重探討集群環(huán)境中的ORM緩存同步方案。
2.1 集中式ORM緩存同步方案設計
通??磥恚琌RM框架負責ORM緩存的管理工作,即緩存項的創(chuàng)建,更新,讀取,刪除。ORM緩存和框架運行在同一臺服務器中,對緩存的操作都在本地完成。使用集中式ORM緩存同步方案,其基本思路是將ORM緩存和管理與緩存數(shù)據(jù)的使用分離開來,ORM集群中部署一臺專用的計算機用于數(shù)據(jù)緩存以及對緩存的管理,應用服務器不在本地保存數(shù)據(jù)的緩存。圖4簡要說明了該設計方案。所有服務器連接到緩存服務器,緩存服務器再連接到數(shù)據(jù)庫服務器。所有的Web服務器對于數(shù)據(jù)的請求,都必須經(jīng)由緩存服務器處理,緩存服務器還起到了代理作用。
對于應用系統(tǒng)數(shù)據(jù)操作來說(以讀取數(shù)據(jù)為例),Web服務器首先向緩存服務器請求數(shù)據(jù),緩存服務器在ORM緩存中執(zhí)行數(shù)據(jù)查找,試圖找到滿足條件的數(shù)據(jù)項;如果能從緩存中找到滿足要求的數(shù)據(jù)項則返回給提出請求的應用服務器,否則向數(shù)據(jù)庫提交一個數(shù)據(jù)查詢請求,將獲取的數(shù)據(jù)添加到緩存中并交付給提出請求的應用系統(tǒng)。所有應用所看到的數(shù)據(jù)緩存是一份唯一的副本,完全可以滿足一致性要求。與直接訪問數(shù)據(jù)庫相比,集中式訪問緩存管理只需網(wǎng)絡IO,無需訪問數(shù)據(jù)庫時產(chǎn)生的磁盤IO,可以明顯提升性能。但集中式管理也存在一些問題。
所有應用連接到緩存服務器,當應用系統(tǒng)集群中服務器有較大數(shù)量時,必然會對緩存服務器產(chǎn)生極大的負載,緩存服務器可能會成為集群系統(tǒng)的IO瓶頸,從而影響整個系統(tǒng)的性能。緩存服務器也必須保證具有高可用性,該服務器的故障會導致整個集群系統(tǒng)癱瘓。當多個應用同時請求同一數(shù)據(jù)時,也會存在現(xiàn)在、潛在的不一致風險。為解決上述問題,可在緩存中引入鎖的概念。對于每一個緩存數(shù)據(jù)項的請求都必須加鎖。鎖分為兩種,即共享鎖和排他鎖。其中共享鎖之間相容,排他鎖與共享鎖,排他鎖和排他鎖之間不相容[7]。對于緩存數(shù)據(jù)的請求,必須被授予鎖之后才能被滿足。只讀請求建議申請共享鎖,而對數(shù)據(jù)的修改必須申請排他鎖。數(shù)據(jù)使用完畢后必須要及時釋放占有的鎖,以便對該緩存項的后續(xù)請求予以滿足。由于鎖的沖突,不同應用服務器對于同一緩存項的大量讀寫請求也會成為潛在的性能瓶頸,大量的鎖請求對于集群中不同的服務器來說可能會造成死鎖[8],使系統(tǒng)無法正常運行。由于對緩存項鎖的管理由緩存服務器進行,可以采用各種死鎖預防和死鎖檢測及恢復算法進行處理[9]。
2.2 分布式ORM緩存同步方案設計
與集中式ORM緩存同步方案不同,分布式方案不存在一個專門用于管理緩存的服務器。分布式部署方案同圖3中的部署方式類似,不同之處在于每臺Web服務器之間緩存的同步方式。對于每一臺應用服務器而言,其ORM緩存都存儲在本地,通過本地管理來滿足對緩存的需求,同時緩存管理也可以同時與其他集群中的應用服務器協(xié)調(diào)以實現(xiàn)緩存同步。緩存同步方案如下所示:
監(jiān)視數(shù)據(jù)庫元組改動:該方案的基本原理是所有應用服務器監(jiān)視對數(shù)據(jù)庫的操作。數(shù)據(jù)庫操作分為查詢,刪除,增加和修改。查詢操作不會改動關系中的元組,故不會使ORM緩存數(shù)據(jù)與數(shù)據(jù)庫中的數(shù)據(jù)不一致。增加操作雖然會更新數(shù)據(jù)庫的狀態(tài),但增加的記錄必然只存在于數(shù)據(jù)庫中和執(zhí)行增加操作的應用服務器緩存中,且兩者同步,其他服務器不會緩存該新增數(shù)據(jù)。修改和刪除操作會影響到緩存了該元組的其他應用服務器,導致緩存和數(shù)據(jù)庫中的記錄不一致。這時數(shù)據(jù)庫應當通知所有集群中的應用服務器,使該緩存中的數(shù)據(jù)無效。將數(shù)據(jù)的改動通知給應用服務器,以Oracle數(shù)據(jù)庫為例,可以使用如下方案,其基本思想用到了觀察者模式。觀察者模式定義了對象間的一對多依賴關系,當一個對象的狀態(tài)發(fā)生改變時,所有依賴它的對象都會得到通知并被自動更新[10]??梢葬槍racle數(shù)據(jù)庫中涉及的表編寫觸發(fā)器,當更新和刪除操作發(fā)生時通知所有Web服務器,以將涉及修改或刪除的元組從緩存中移除。發(fā)送通知的消息中可以包含所有涉及元組的主鍵。Oracle數(shù)據(jù)庫可以掛接Java程序并被PLSQL調(diào)用[11],掛接的Java程序通過網(wǎng)絡完成后續(xù)的消息發(fā)送。
更新操作通知所有應用系統(tǒng):針對系統(tǒng)的修改和刪除操作,發(fā)起操作的服務器在完成數(shù)據(jù)庫操作后,立即將消息發(fā)送給所有集群中的應用系統(tǒng),收到消息的應用系統(tǒng)從緩存中移除指定的緩存對象,使得緩存中不存在與數(shù)據(jù)庫不一致的對象。該方案與前一方案相比,前者對數(shù)據(jù)庫的負載較小,所有消息的發(fā)送路徑均建立在應用服務器之間,數(shù)據(jù)庫不參與同步過程。但當應用服務器之間由于網(wǎng)絡或安全策略而無法相互通信時,該方案則無法實現(xiàn)同步信息的交互。
2.3 ORM緩存同步方案設計對比
對于系統(tǒng)部署來說,集中式方案需要額外部署一臺緩存服務器,且所有數(shù)據(jù)訪問都需經(jīng)由該服務器來完成,對該服務器壓力較大,但便于系統(tǒng)的開發(fā)和管理。分布式方案中監(jiān)視數(shù)據(jù)庫的方案對于數(shù)據(jù)庫服務器會產(chǎn)生額外的負載,使得本來在性能上捉襟見肘的數(shù)據(jù)庫擁有更大的壓力,雖然如此,但其卻具有良好的可靠性。通知所有系統(tǒng)的方案可以將同步工作分攤到所有應用系統(tǒng)服務器上,但對于網(wǎng)絡帶寬及穩(wěn)定性有著較高的要求,由于網(wǎng)絡而未收到消息會導致數(shù)據(jù)出現(xiàn)不一致的可能。
3 結 語
在應用系統(tǒng)集群中使用ORM技術并開啟緩存,通過分析緩存不一致現(xiàn)象找出產(chǎn)生該問題的原因。筆者提出了集中式和分布式緩存兩種設計方案,試圖解決該問題。通過對比各種方案的特點及其適用環(huán)境,為實現(xiàn)緩存一致性提供了思路和方法。
參考文獻
[1]查修齊,吳榮泉,高元鈞. C/S到B/S模式轉(zhuǎn)換的技術研究[J].計算機工程, 2014, 40(1):263-267.
[2]張鳳娟,冉寶才,劉麗.基于B_S模式的醫(yī)院運維管理系統(tǒng)設計與實現(xiàn)[J].醫(yī)學信息學雜志,2016,37 (7):26-30.
[3]陳正舉.基于HIBERNATE的數(shù)據(jù)庫訪問優(yōu)化[J].計算機應用與軟件, 2012, 29(7):144-149.
[4]朱保鋒, 蔡艷.計算機系統(tǒng)中不同組織方式的Cache性能分析[J].河南教育學院學報(自然科學版), 2012, 21(1):38-39.
[5]曹偉,應君,董黎剛. Hibernate的緩存機制及其應用的研究[J].杭州電子科技大學學報,2013,33(5):158-161.
[6]張吉良, 陳德宗.服務器集群環(huán)境下Hibernate使用問題和解決方案[J].科技資訊, 2011(23):14.
[7] Abraham Silberschatz, Henry F.Korth, S.Sudrashan. Database System Concepts[M].New York: McGraw-Hill, 2011: 661.
[8]孫國營.計算機中的死鎖問題[J].科技傳播, 2010(12).
[9]塔嫩鮑姆.現(xiàn)代操作系統(tǒng)[M].北京:機械工業(yè)出版社,2009: 248.
[10] Erich Gamma, Richard Helm, Ralph Johnson, et al.設計模式-可復用面向?qū)ο筌浖幕A[M].北京:機械工業(yè)出版社,2012.
[11]全立新.使用Java編寫Oracle存儲過程[J].計算機時代,2004 (6):35-36.endprint