首頁 >後端開發 >C#.Net教程 >關於hibernate快取的問題:

關於hibernate快取的問題:

巴扎黑
巴扎黑原創
2016-12-20 14:19:271056瀏覽

1.     關於hibernate快取的問題:
1.1.1.        基本的快取原理
Hibernate快取分為二級,而第一級存放於session中稱為一級快取,預設有且無法卸載。
 
第二級是由sessionFactory控制的進程級快取。是全域共享的緩存,凡是會呼叫二級快取的查詢方法 都會從中受益。只有經過正確的配置後二級快取才會發揮作用。同時在進行條件查詢時必須使用對應的方法才能從快取中取得資料。例如Query.iterate()方法、load、get方法等。必須注意的是session.find方法永遠是從資料庫中獲取數據,不會從二級快取中獲取數據,即便其中有其所需的數據也是如此。
 
查詢時使用快取的實現過程為:首先查詢一級快取中是否具有需要的數據,如果沒有,查詢二級緩存,如果二級快取中也沒有,此時再執行查詢資料庫的工作。要注意的是:此3種方式的查詢速度是依序降低的。
1.2.   存在的問題
1.2.1.      一級快取的問題以及使用二級快取的原因
     因為Session的生命期往往很短,而存在於Session內部的第一級最快快取的生命期當然也很短,存在於Session內部的第一級最快的生命期當然也短,所以第一級快取的命中率是很低的。其對系統性能的改善也是很有限的。當然,這個Session內部快取的主要功能是保持Session內部資料狀態同步。並非是hibernate為了大幅提高系統效能所提供的。
為了提高使用hibernate的效能,除了常規的一些需要注意的方法例如:
使用延遲載入、迫切外連接、查詢過濾等以外,還需要配置hibernate的二級快取。其對系統整體性能的改善往往具有立竿見影的效果!
(經過自己先前作專案的經驗,一般會有3~4倍的效能提升)
 
1.2.2.      N+1次查詢的問題
執行條件查詢時,iterate()方法具有著名的「n+ 1」次查詢的問題,也就是說在第一次查詢時iterate方法會執行滿足條件的查詢結果數再加一次(n+1)的查詢。但是此問題只存在於第一次查詢時,在後面執行相同查詢時效能會得到極大的改善。此方法適合查詢資料量較大的業務資料。
但是注意:當資料量特別大時(例如管線資料等)需要針對此持久化物件配置其特定的快取策略,例如設定其存在於快取中的最大記錄數、快取存在的時間等參數,以避免系統將大量的資料同時裝載入記憶體中引起記憶體資源的快速耗盡,反而降低系統的效能! ! !
 
1.3.   使用hibernate二級快取的其他注意事項:
1.3.1.      關於資料的有效性
另外,hibernate會自行維護以確保快取中的資料和資料庫中的真實資料的一致性!無論何時,當你呼叫save()、update()或saveOrUpdate()方法傳遞一個物件時,或使用load()、 get()、list()、iterate() 或scroll()方法取得一個物件時, 該物件都將被加入到Session的內部快取中。 當隨後flush()方法被呼叫時,物件的狀態會和資料庫取得同步。
 
也就是說刪除、更新、增加資料的時候,同時更新快取。當然這也包括二級緩存!
 
只要是呼叫hibernate API執行資料庫相關的工作。 hibernate都會為你自動保證 快取資料的有效性! !
 
但是,如果你使用了JDBC繞過hibernate直接執行對資料庫的操作。此時,Hibernate不會/也不可能自行感知到資料庫被進行的變化改動,也就不能再保證快取中資料的有效性! !
 
這也是所有的ORM產品共同具有的問題。幸運的是,Hibernate為我們揭露了Cache的清除方法,這給了我們一個手動保證資料有效性的機會! !
一級緩存,二級緩存都有相應的清除方法。
 
其中二級快取提供的清除方法為:
按物件class清空快取
               
   

1.3.2.      適合使用的情況
並非所有的情況都適合於使用二級緩存,需要根據具體情況來決定。同時可以針對某一個持久化物件配置其具體的快取策略。
 
適合使用二級快取的情況:
1、資料不會被第三方修改;
 
一般情況下,會被hibernate以外修改的資料最好不要配置二級緩存,以免造成不一致的資料。但如果此資料因為效能的原因需要被緩存,同時又有可能被第3方例如SQL修改,也可以為其配置二級快取。只是此時需要在sql執行修改後手動呼叫cache的清除方法。以確保資料的一致性
 
  2、資料大小在可接收範圍之內;
 
     如果資料表資料量特別龐大,此時不適合於二級快取。原因是快取的資料量過大可能會造成記憶體資源緊張,反而降低效能。
 
如果資料表資料量特別龐大,但是經常使用的往往只是較新的那部分資料。此時,也可為其配置二級快取。但必須單獨配置其持久化類別的快取策略,例如最大快取數、快取過期時間等,將這些參數降低至合理的範圍(太高會造成記憶體資源緊張,太低了快取的意義不大)。
 
  3、數據更新頻率低;
 
     對於數據更新頻率過高的數據,頻繁同步緩存中數據的代價可能和 查詢緩存中的數據從中獲得的好處相當,壞處益處相抵消。此時快取的意義也不大。
 
 
  4、非關鍵數據(不是財務數據等)
 
  財務數據等是非常重要的數據,絕對不允許出現或使用無效的數據,所以此時為了安全起見最好不要使用二級緩存。
  因為此時 「正確性」的重要性遠大於 「高績效」的重要性。
 
2.     在系統中使用hibernate快取的建議
1.4.   目前狀況
 一般系統中有三種情況會繞過hibernate執行資料庫操作:

1、多個應用系統同時存取一個資料庫使用資料來源二級快取會不可避免的造成資料不一致的問題,
   此時要進行詳細的設計。例如在設計上避免對相同資料表的同時的寫入操作,
   使用資料庫各種層級的鎖定機制等。
 
2、動態表相關

   所謂「動態表」是指在系統運作時會根據使用者的作業系統自動建立的資料表。

   例如「自訂表單」等屬於使用者自訂擴充功能開發性質的功能模組,因為此時資料表是執行時間建立的,所以不能進行hibernate的對應。因此對它的操作只能是繞過hibernate的直接資料庫JDBC操作。

      如果此時動態表中的資料沒有設計緩存,就沒有資料不一致的問題。

   如果此時自行設計了快​​取機制,則呼叫自己的快取同步方法即可。

3、使用sql對hibernate持久化物件表進行批次刪除時

     此時執行批次刪除後,快取中會存在已刪除的資料。

分析: 
   當執行了第3條(sql批次刪除)後,後續的查詢只可能是以下三種方式:
a. session.find()方法:
根據前面的總結,find方法不會查詢二級快取的數據,而是直接查詢資料庫。
所以不存在數據有效性的問題。
b. 呼叫iterate方法執行條件查詢時:
根據iterate查詢方法的執行方式,其每次都會到數據庫中查詢滿足條件的id值,然後再根據此id 到緩存中獲取數據,當緩存中沒有此id的資料才會執行資料庫查詢;
如果此記錄已被sql直接刪除,則iterate在執行id查詢時不會將此id查詢出來。所以,即便快取中有此筆記錄也不會被客戶取得,也就不存在不一致的情況。 (此情況經過測試驗證)
 
c. 用get或load方法按id執行查詢:
 
客觀上此時會查詢得到已過期的資料。但又因為系統中執行sql批量刪除一般是

針對中間關聯資料表,對於

中間關聯表的查詢通常都是採用條件查詢,按id來查詢某一條關聯關係的幾率很低,所以此問題也不存在!
 
   如果某個值對象確實需要按id查詢一條關聯關係,同時又因為資料量大使用了sql執行批次刪除。當滿足此兩個條件時,為了保證按id 的查詢得到正確的結果,可以使用手動清楚二級緩存中此對象的數據的方法!!
(此種情況出現的可能性較小)
 
1.5 .   建議
1、建議不要使用sql直接執行資料持久化物件的資料的更新,但是可以執行批次刪除。 (系統中需要批次更新的地方也較少)
 
2、如果必須使用sql執行資料的更新,必須清空此物件的快取資料。呼叫
SessionFactory.evict(class) 
SessionFactory.evict(class,id)
等方法。
 
3、在批次刪除資料量不大的時候可以直接採用hibernate的批次刪除,這樣就不存在繞開hibernate執行sql產生的快取資料一致性的問題。
 
4、不建議採用hibernate的批次刪除方法來刪除大量的記錄資料。
原因是hibernate的批次刪除會執行1個查詢語句外加 滿足條件的n條刪除語句。而不是一次執行一條條件刪除語句! ! 
當待刪除的資料很多時會有很大的效能瓶頸! ! !如果大量刪除資料量較大,例如超過50條,可以採用JDBC直接刪除。這樣作的好處是只執行一條sql刪除語句,效能會有很大的改善。同時,快取資料同步的問題,可以採用 hibernate清除二級快取中的相關資料的方法。
呼叫 SessionFactory.evict(class) ;SessionFactory.evict(class,id)等方法。
 
所以說,對於一般的應用系統開發而言(不涉及到集群,分佈式資料同步問題等),因為只在中間關聯表執行批量刪除時調用了sql執行,同時中間關聯表一般是執行條件查詢不太可能執行按id查詢。所以,此時可以直接執行sql刪除,甚至不需要呼叫快取的清除方法。這樣做不會導致日後配置了二級快取造成資料有效性的問題。
 
退一步說,即使以後真的呼叫了按id查詢中間表物件的方法,也可以透過呼叫清除快取的方法來解決。

 

4、具體的配置方法 
根據我了解的很多hibernate的用戶在調用其相應方法時都迷信的相信“hibernate會自行為我們處理性能的問題”,或者“hibernate會自動為我們的所有操作調用緩存”,實際的情況是hibernate雖然為我們提供了很好的緩存機制和擴展緩存框架的支持,但是必須經過正確的調用其才有可能發揮作用! !所以造成很多使用hibernate的系統的效能問題,其實並不是hibernate不行或不好,而是因為使用者沒有正確的了解其使用方法造成的。相反,如果配置得當hibernate的性能表現會讓你有相當“驚喜的”發現。下面我說明特定的設定方法.

 ibernate提供了二級快取的介面: 
net.sf.hibernate.cache.Provider, 
同時提供了一個預設的實作net.sf.hibernate.cache.HashtableCacheProvider, 可以配置其他的實作例如ehcache,jbosscache等。

具體的設定位置位於hibernate.cfg.xml檔案中 

true 
natesf. .cache.HashtableCacheProvider

很多的hibernate使用者在設定到這一步驟就以為完事了, 

注意:其實光這樣配,根本就沒有使用hibernate的二級快取。同時因為他們在使用hibernate時大多時候是馬上關閉session,所以,一級快取也沒有起到任何作用。結果就是沒有使用任何緩存,所有的hibernate操作都是直接操作的資料庫! !性能可以想見。

正確的方法是除了以上的配置外還應該配置每個vo物件的特定快取策略,在影射檔案中配置。例如:

 

 
 
 name="id" column="TYPEID" type="java.lang.Long"> 
 

 
 
 


關鍵就是這個,其有幾個選擇 
read-only,read-write,transactional,等 
然後在執行查詢時注意了,如果是條件查詢,或傳回所有結果的查詢,此時session.find()方法不會取得快取中的資料。只有在呼叫query.iterate()方法時才會調快取的資料。

同時get 和load方法是都會查詢快取中的資料.

對於不同的快取框架具體的配置方法會有不同,但是大體是以上的配置

(另外,對於支援事務型,以及支援叢集的環境的配置我會爭取在後續的文章中發表出來)

 

3.     總結
總之是根據不同的業務狀況和專案狀況對hibernate進行有效的配置和正確的使用,揚長避短。不存在適合於任何情況的一個「萬能」的方案。


陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn