程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> JAVA Hibernate緩存詳解

JAVA Hibernate緩存詳解

編輯:關於JAVA

1、緩存介紹

Hibernate中提供了兩級Cache,第一級別的緩存是Session級別的緩存,它是屬於事務范圍的緩存。這 一級別的緩存由hibernate管理的,一般情況下無需進行干預;第二級別的緩存是SessionFactory級別的 緩存,它是屬於進程范圍或群集范圍的緩存。這一級別的緩存可以進行配置和更改,並且可以動態加載和 卸載。 Hibernate還為查詢結果提供了一個查詢緩存,它依賴於第二級緩存。

一. 一級緩存和二級緩存的比較:

第一級緩存和第二級緩是存放數據的形式是以相互關聯的持久化對象,對象的散裝數據等方式存儲的 。每個事務都有單獨的第一級緩存,緩存被同一個進程或集群范圍內的所有事務共享,由於每個事務都擁 有單獨的第一級緩存,不會出現並發問題,無需提供並發訪問策略,由於多個事務會同時訪問第二級緩存 中相同數據,因此必須提供適當的並發訪問策略,來保證特定的事務隔離級別,沒有提供數據過期策略。 處於一級緩存中的對象永遠不會過期,除非應用程序顯式清空緩存或者清除特定的對象,所以必須提供數 據過期策略,數據過期策略如:基於內存的緩存中的對象的最大數目,允許對象處於緩存中的最長時間, 以及允許對象處於緩存中的最長空閒時間,物理存儲介質內存,內存和硬盤。對象的散裝數據首先存放在 基於內存的緩存中,當內存中對象的數目達到數據過期策略中指定上限時,就會把其余的對象寫入基於硬 盤的緩存中。緩存的軟件實現在Hibernate的Session的實現中包含了緩存的實現由第三方提供, Hibernate僅提供了緩存適配器(CacheProvider)。用於把特定的緩存插件集成到Hibernate中。啟用緩存 的方式只要應用程序通過Session接口來執行保存、更新、刪除、加載和查詢數據庫數據的操作, Hibernate就會啟用第一級緩存,把數據庫中的數據以對象的形式拷貝到緩存中,對於批量更新和批量刪 除操作,如果不希望啟用第一級緩存,可以繞過Hibernate API,直接通過JDBC API來執行指操作。用戶 可以在單個類或類的單個集合的粒度上配置第二級緩存。如果類的實例被經常讀但很少被修改,就可以考 慮使用第二級緩存。只有為某個類或集合配置了第二級緩存,Hibernate在運行時才會把它的實例加入到 第二級緩存中。用戶管理緩存的方式:第一級緩存的物理介質為內存,由於內存容量有限,必須通過恰當 的檢索策略和檢索方式來限制加載對象的數目。Session的 evit()方法可以顯式清空緩存中特定對象,但 這種方法不值得推薦。第二級緩存的物理介質可以是內存和硬盤,因此第二級緩存可以存放大量的數據, 數據過期策略的maxElementsInMemory屬性值可以控制內存中的對象數目。管理第二級緩存主要包括兩個 方面:選擇需要使用第二級緩存的持久類,設置合適的並發訪問策略:選擇緩存適配器,設置合適的數據 過期策略。

二. 一級緩存的管理:

當應用程序調用Session的save()、update()、savaeOrUpdate()、get()或load(),以及調用查詢接口 的 list()、iterate()或filter()方法時,如果在Session緩存中還不存在相應的對象,Hibernate就會把 該對象加入到第一級緩存中。當清理緩存時,Hibernate會根據緩存中對象的狀態變化來同步更新數據庫 。 Session為應用程序提供了兩個管理緩存的方法: evict(Object obj):從緩存中清除參數指定的持久 化對象。 clear():清空緩存中所有持久化對象。

三. Hibernate二級緩存的管理:

1. Hibernate二級緩存策略的一般過程如下:

1) 條件查詢的時候,總是發出一條select * from table_name where …. (選擇所有字段)這樣的SQL語句查詢數據庫,一次獲得所有的數據對象。

2) 把獲得的所有數據對象根據ID放入到第二級緩存中。

3) 當Hibernate根據ID訪問數據對象的時 候,首先從Session一級緩存中查;查不到,如果配置了二級緩存,那麼從二級緩存中查;查不到,再查 詢數據庫,把結果按照ID放入到緩存。

4) 刪除、更新、增加數據的時候,同時更新緩存。

Hibernate二級緩存策略,是針對於ID查詢的緩存策略,對於條件查詢則毫無作用。為此,Hibernate 提供了針對條件查詢的Query Cache。

2. 什麼樣的數據適合存放到第二級緩存中?

1) 很少被修改的數據

2) 不是很重要的數據,允 許出現偶爾並發的數據

3) 不會被並發訪問的數據

4) 參考數據,指的是供應用參考的常量數據, 它的實例數目有限,它的實例會被許多其他類的實例引用,實例極少或者從來不會被修改。

3. 不適合存放到第二級緩存的數據?

1) 經常被修改的數據

2) 財務數據,絕對不允許出現並 發

3) 與其他應用共享的數據。

4. 常用的緩存插件 Hibernater二級緩存是一個插件,下面是幾種常用的緩存插件:

◆EhCache: 可作為進程范圍的緩存,存放數據的物理介質可以是內存或硬盤,對Hibernate的查詢緩存提供了支持。

◆OSCache:可作為進程范圍的緩存,存放數據的物理介質可以是內存或硬盤,提供了豐富的緩存數據 過期策略,對Hibernate的查詢緩存提供了支持。

◆SwarmCache:可作為群集范圍內的緩存,但不支持 Hibernate的查詢緩存。

◆JBossCache:可作為群集范圍內的緩存,支持事務型並發訪問策略,對 Hibernate的查詢緩存提供了支持。

5. 配置Hibernate二級緩存的主要步驟:

1) 選擇需要使用二級緩存的持久化類,設置它的命名緩 存的並發訪問策略。這是最值得認真考慮的步驟。

2) 選擇合適的緩存插件,然後編輯該插件的配置文 件。

二、緩存應用的場景:

1、對於新聞,論壇,博客等互聯網應用適合在前端做緩存,比如url做為 key來緩存整個頁面的內容。一條新聞a被如前所述的緩存起來了,在網站並發訪問量大時,會大大提高網 站的吞吐能力。好了現再須要編輯這條新聞,如何同步更新緩存呢?須要立即同步更新緩存嗎?不須要, 互聯網應用允許用戶在5-10分鐘之後再看到更新之後的新聞,這是可以接受的。沒有較高的時效性,允許 延遲。這樣我們設定緩存對象的最大生命時間為10分鐘,一個被緩存的對象存活時間超過10分鐘就被清理 ,當新的訪問請求到來時,再從數據庫中加載他,再次被緩存10分鐘。Hibernate二級緩存不適合這個場 景,這個場景對緩存的鎖、事務沒有要求,對高並發,高數據量有要求。

總結一下:被緩存的對象沒 有較高的時效性,允許對象更新後延遲(10分鐘內)展示,允許(10分鐘內)的數據不一致。

2、對於企業應用,要保證數據的一致性是第一位的,即使數據被修改,最終用戶看到的數據與數據庫 中的數據要時時一致。適合在應用程序持久層上做緩存,Hibernate二級緩存就適合這個場景。

總結一 下:這個場景對緩存的鎖,事務要求是第一的。對高並發高數據量的要求是第二的,通過鎖保證數據的一 致性。Hibernate對數據庫是獨占的,修改給數據庫的操作都通過他.

三、頻繁更新的數據要不要被緩存:

網上有人說頻繁更新的數據不適合使用緩存。這樣說是不全面 的,因為他少說了前提條件。

數據一致性:本文章的數據一致性是指緩存中的數據與數據庫中的數據就保持一致,嚴格的一致。決 對沒有髒數據。

當你須要數據一致性,而又不能保存數據一致性時,頻繁更新的數據就不可以被緩存。不緩存直接操 作數據庫,就一致了,沒有不一致的問題了。

你的Hibernate對數據庫不是獨占的,有其它程序來修改數據庫中的記錄,這時Hibernate是不知道的,也 會發生數據不一致.

當使用url或sql語句做為KEY來緩存時,一句select 語句查出n個對象,無法在緩存中精准的找到被修 改的某一個對象,當修改一個對象時就不能在緩存中精准的找到他,為了保證數據一致性,就要清除緩存 中所有的同類對象,使下次查詢時無法命中緩存。而不清除,很有可能發生數據不一致。這相當於 Hibernate的查詢緩存。這時個不要使用緩存。

當你須要數據一致性,而又能保存數據一致性時,頻繁更新的數據是可以被緩存的。這裡我們使 用緩存的“鎖”機制來保證,你使用的Hibernate第三方緩存要支持“鎖”,就是 read-write模式。這是重點啊。第三方緩存鎖的實現方法不同性能也不同,鎖是緩存性能下降第一原因, 一定要使用高性能的鎖,這就要了解多款Hibernate第三方緩存.

因為經常被更新修改的對象,一定也更加經常的被查詢,需要緩存他來提高應用程序的性能。如果執 行修改sql時,同時鎖住緩存中的這個對象並更新他,之後解鎖是最理想的。Hibernate的二級緩存策略, 是針對於ID查詢的緩存策略,所以可以做到精准的找到緩存中的目標,加之“鎖”的幫助,可 實現數據一致性。

但限制也有比如使用HQL時就不能精准的找到緩存中的目標,只好清除緩存中所有的同類對象來保證數 據的一致性(緩存中沒數據就沒一致性問題了)。

查看本欄目

四、Hibernate二級緩存中的對象什麼時候會被清理:

在read-write模式下:

我們有一個Order對象 ,是一個實體對象,對應數據庫中order表中的一條記錄,經過查詢已有n個Order對象被放入二級緩存中 。現在我們要修改order表中任意任x條記錄,執行以下HQL:

template.bulkUpdate("update Order set owner = ? where id in (?,?,?)");

這時Hibernate會直接將二級緩存中的n個Order對象清除掉。 天啊,居然不是你想像的修改誰就同步 更新二級緩存中的誰,而是清除了二級緩存中全部的Order類型的對象。為什麼?這一切是為了保證 “數據一致性”。你執行了HQL修改了order表中的x條記錄,這x條是哪幾條?如果sql是子查 詢:update Order set owner =? where id in(select id from *** ),誰知道你修改了order表中的哪 幾條記錄,你自己都不知道,Hibernate更不知道了。所以為了保證二級緩存中的數據與order表中的數據 一致,只能清除了二級緩存中全部的Order類型的對象。二級緩存頻繁的載入與清除,這樣緩存命中率就 會下降。

試驗:

看到這裡後,我很擔心,這樣命中率下降後,沒有起到緩存的作用。今天特意做一個實驗, 看看被緩存的對象在被修改後會怎樣。

環境:Hibernate3.4 , OsCache(usage="read-write"),JUnit

緩存狀態:Hibernate二 級緩存中已緩存了5個Order對象。

測試結果:

1 使用saveOrUpdate()方法更新一個實體對象a時, 新的a對象被put到二級緩存中,同時寫入數據庫,二級緩存中的其它4個Order對象沒有變化。

這時再 查詢這5個Order對象中的任意,是可以命中二級緩存的。

2 使用HQL "update Order set name = ? where id =?" 方法更新一個實體對象a時,所有Order對象被從二級緩存中清除,同時a對象被寫 入數據庫。

這時再查詢這5個Order對象中的任意,無法命中二級緩存,會去查數據庫,查出來的對象 又put進二級緩存。

Hibernate的二級緩存策略,是以ID做為key 的緩存策略,在刪除、更新、增加數據的時候,同時更新 緩存。

對於條件查詢,條件修改,條件刪除(一般是執行HQL)則起不到緩存的作用。條件修改,條件 刪除時(一般是執行HQL)會清空所有在緩存中的同類對象。

為此,Hibernate提供了針對條件查詢的 Query Cache,其實它並不好用。

關於是否命中,是使用Statistics類監測的(通過SessionFactory的getStatistics()方法得到)。

總結一下:如果你打算開啟hibernate的二級緩存,在修改與刪除時,就要使用session.update (),session.delete()方法按ID一條一條的操作,這樣對二級緩存是最優的。

但循環中使用 sesion.update(),session.delete()方法,會產生多條sql語句,原本使用一條HQL完成的工作,現在要執 行多條,你擔心Hibernate與數據庫服務器的網絡通信次數嗎?其實這多條sql是使用JDBC的批處理一次發 送到數據庫服務器的,所以你不用擔心。現在到了數據庫服務器端,我們以oracle為例,oracle要執行多 條sql,就要進行多次的“分析sql語句的正確性,並解析成oracle的原子操作,並制定執行計劃 ”,你擔心這“多次”分析會給oracle帶來性能的影響嗎?不用擔心,請使用oracle的 綁定參數,就是Hibernate中的?代替參數。

五、Hibernate二級緩存的並發策略你了解嗎:

1 只讀緩存 read only

不須要鎖與事務,因為緩存自數據從數據庫加載後就不會改變。

如果數據是只讀的,例如引用數據,那麼總是使用“read-only”策略,因為它是最簡單、 最高效的策略,也是集群安全的策略。是性能第一的策略 。

2 讀寫緩存 read write

對緩存的更新發生在數據庫事務完成後。緩存需要支持鎖。

在一個事務 中更新數據庫,在這個事務成功完成後更新緩存,並釋放鎖。

鎖只是一種特定的緩存值失效表述方式 ,在它獲得新數據庫值前阻止其他事務讀寫緩存。那些事務會轉而直接讀取數據庫。

緩存必須支持鎖 ,事務支持則不是必須的。如果緩存是一個集群,“更新緩存”的調用會將新值推送給所有副 本,這通常被稱為“推(push)”更新策略。

如果你的數據是又讀又寫的,那麼使用“read-write”策略。這通常是性能第三的策略, 因為它要求有緩存鎖,緩存集群中使用重量級的“推”更新策略。

3 非嚴格讀寫緩存 nonstrict read write

在一個事務中更新數據庫,在這個事務完成前就清除緩 存,為了安全起見,無論事務成功與否,在事務完成後再次清除緩存。

既不需要支持緩存鎖,也不需 要支持事務。如果是緩存集群,“清除緩存”調用會讓所有副本都失效,這通常被稱為 “拉(pull)”更新策略。

如果你的數據讀很多或者很少有並發緩存訪問和更新,那麼可以使用“nonstrict-read- write”策略。感謝它的輕量級“拉”更新策略,它通常是性能第二好的策略。

4 事務緩存 transactional (一定要在JTA環境中)

對緩存和數據庫的更新被包裝在同一個JTA事 務中,這樣緩存與數據庫總是保持同步的。數據庫和緩存都必須支持JTA。

除非你真的想將緩存更新和數據庫更新放在一個JTA事務裡,否則不要使用 “transactional”策略,因為JTA需要漫長的兩階段提交處理,這導致它基本是性能最差的策 略。

六、緩存鎖的性能也要了解,知道加了鎖後性能會下降:

為了保證數據的安全性,不發生髒數據,各 個緩存通常使用鎖來保證

在本地方式運行時,緩存最大的開銷就是使用鎖來在保證共享數據完整性。

在集群環境中,RPC調用,鎖,是性能上大開銷。

下面以JBoss Cache為例說一說鎖:

JBoss Cache1.* 和 2.* 時代,提供樂觀鎖,悲觀鎖,但是性 能不高。

JBoss Cache3.0 MVCC鎖方案性能很高。

悲觀鎖:這些鎖的隔離級別和數據庫實施的隔離級別相同,這種方案簡單而且健壯,允許多用戶同時 讀取數據。讀操作阻塞寫操作,悲觀鎖的讀寫是互斥的,無法同時進行的,寫的性能不好。

樂觀鎖:這個方式則牽涉到數據版本,可以獲得高度並發性。那些請求讀取數據的用戶不會因為並發 數據庫寫入操作而受到阻塞。而且,樂觀鎖定方式還可以避免悲觀鎖定中有可能發生的死鎖。但它仍然有 兩個主要的缺點:一是性能問題。因為不斷的將結點的狀態拷貝到每個並發線程所造成的內存和 CPU 開 銷是不容忽略的。二是盡管並發時允許了寫操作,但是一旦發現數據的版本不對,事務提交時不可避免的 還是會失敗。也就是說,此時寫事務雖然可以不受限制的進行大量處理和寫操作,但是這樣在事務結束的 時候容易出現提交失敗。

多版本並發控制(MVCC):在數據訪問速度上較之前者也勝出百倍。MVCC 提供了非阻塞 (non- blocking) 讀操作 ( 它並不會去阻塞 wirter threads) ,在避免死鎖的同時也提供了更高級的並發機制 。更棒的是,我們的 MVCC 實現甚至可以對 reader threads 完全不采用任何鎖 ( 對於像緩存這樣頻繁 讀取的系統來說,意義太大了 ) ,

七、批量處理時請不要使用二級緩存

當你執行大量的 添加與修改時,並且這個實體對象被配置為 啟用二級緩存,你考慮過二級緩存會怎麼樣嗎?請看下面代碼:

Session session = sessionFactory.openSession(); Transaction tx = session.beginTransaction(); for ( int i=0; i<100000; i++ ) { Customer customer = new Customer(.....); //如果你的 hibernate.cache.use_second_level_cache 是 true, 請在會話級別上關 閉他 //向(任何一級)緩存中加載大量數據通常也意味著它們很快會被清除出去,這會增加GC開銷。 session.setCacheMode(CacheMode.IGNORE); session.save(customer); if ( i % 50 == 0 ) { //將本 批插入的對象立即寫入數據庫並釋放內存 session.flush(); session.clear(); } } tx.commit(); session.close();

批處理通常不需要數據緩存,否則你會將內存耗盡並大量增加GC開銷。如果內存有限,那這種情況會 很明顯。

八、了解幾種優秀緩存方案:

1、Memcached

分布式緩存系統,memcached 要求set的對象必須是可 序列化對象,jboss cache等java obect cache是沒有這個說法的,這是本質的不同的,但是他可以在網絡上 用,所以必須序列化也可理解。

獨立服務器+java 客戶端。

Memcached java 客戶端有:

memcache-client-forjava,

XMemcached,

spymemcached,

memcache-client-forjava

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved