1. 程式人生 > >這是我看過最好的對hibernate的二級快取解析

這是我看過最好的對hibernate的二級快取解析

很多人對二級快取都不太瞭解,或者是有錯誤的認識,我一直想寫一篇文章介紹一下hibernate的二級快取的,今天終於忍不住了。 
我的經驗主要來自hibernate2.1版本,基本原理和3.0、3.1是一樣的,請原諒我的頑固不化。

hibernate的session提供了一級快取,每個session,對同一個id進行兩次load,不會發送兩條sql給資料庫,但是session關閉的時候,一級快取就失效了。 

二級快取是SessionFactory級別的全域性快取,它底下可以使用不同的快取類庫,比如ehcache、oscache等,需要設定hibernate.cache.provider_class,我們這裡用ehcache,在2.1中就是 

hibernate.cache.provider_class=net.sf.hibernate.cache.EhCacheProvider 
如果使用查詢快取,加上 
hibernate.cache.use_query_cache=true 


快取可以簡單的看成一個Map,通過key在快取裡面找value。 

Class的快取
對於一條記錄,也就是一個PO來說,是根據ID來找的,快取的key就是ID,value是POJO。無論list,load還是iterate,只要讀出一個物件,都會填充快取。但是list不會使用快取,而iterate會先取資料庫select id出來,然後一個id一個id的load,如果在快取裡面有,就從快取取,沒有的話就去資料庫load。假設是讀寫快取,需要設定: 

<cache usage="read-write"/> 
如果你使用的二級快取實現是ehcache的話,需要配置ehcache.xml 
<cache name="com.xxx.pojo.Foo" maxElementsInMemory="500" eternal="false" timeToLiveSeconds="7200" timeToIdleSeconds="3600" overflowToDisk="true" /> 
其中eternal表示快取是不是永遠不超時,timeToLiveSeconds是快取中每個元素(這裡也就是一個POJO)的超時時間,如果eternal="false",超過指定的時間,這個元素就被移走了。timeToIdleSeconds是發呆時間,是可選的。當往快取裡面put的元素超過500個時,如果overflowToDisk="true",就會把快取中的部分資料儲存在硬碟上的臨時檔案裡面。 

每個需要快取的class都要這樣配置。如果你沒有配置,hibernate會在啟動的時候警告你,然後使用defaultCache的配置,這樣多個class會共享一個配置。 
當某個ID通過hibernate修改時,hibernate會知道,於是移除快取。 
這樣大家可能會想,同樣的查詢條件,第一次先list,第二次再iterate,就可以使用到快取了。實際上這是很難的,因為你無法判斷什麼時候是第一次,而且每次查詢的條件通常是不一樣的,假如資料庫裡面有100條記錄,id從1到100,第一次list的時候出了前50個id,第二次iterate的時候卻查詢到30至70號id,那麼30-50是從快取裡面取的,51到70是從資料庫取的,共傳送1+20條sql。所以我一直認為iterate沒有什麼用,總是會有1+N的問題。 
(題外話:有說法說大型查詢用list會把整個結果集裝入記憶體,很慢,而iterate只select id比較好,但是大型查詢總是要分頁查的,誰也不會真的把整個結果集裝進來,假如一頁20條的話,iterate共需要執行21條語句,list雖然選擇若干欄位,比iterate第一條select id語句慢一些,但只有一條語句,不裝入整個結果集hibernate還會根據資料庫方言做優化,比如使用mysql的limit,整體看來應該還是list快。) 
如果想要對list或者iterate查詢的結果快取,就要用到查詢快取了 

查詢快取
首先需要配置hibernate.cache.use_query_cache=true 
如果用ehcache,配置ehcache.xml,注意hibernate3.0以後不是net.sf的包名了 
<cache name="net.sf.hibernate.cache.StandardQueryCache" 
   maxElementsInMemory="50" eternal="false" timeToIdleSeconds="3600" 
   timeToLiveSeconds="7200" overflowToDisk="true"/> 
<cache name="net.sf.hibernate.cache.UpdateTimestampsCache" 
   maxElementsInMemory="5000" eternal="true" overflowToDisk="true"/> 
然後 
query.setCacheable(true);//啟用查詢快取 
query.setCacheRegion("myCacheRegion");//指定要使用的cacheRegion,可選 
第二行指定要使用的cacheRegion是myCacheRegion,即你可以給每個查詢快取做一個單獨的配置,使用setCacheRegion來做這個指定,需要在ehcache.xml裡面配置它: 
<cache name="myCacheRegion" maxElementsInMemory="10" eternal="false" timeToIdleSeconds="3600" timeToLiveSeconds="7200" overflowToDisk="true" /> 
如果省略第二行,不設定cacheRegion的話,那麼會使用上面提到的標準查詢快取的配置,也就是net.sf.hibernate.cache.StandardQueryCache 

對於查詢快取來說,快取的key是根據hql生成的sql,再加上引數,分頁等資訊(可以通過日誌輸出看到,不過它的輸出不是很可讀,最好改一下它的程式碼)。 
比如hql: 
from Cat c where c.name like ? 
生成大致如下的sql: 
select * from cat c where c.name like ? 
引數是"tiger%",那麼查詢快取的key*大約*是這樣的字串(我是憑記憶寫的,並不精確,不過看了也該明白了): 
select * from cat c where c.name like ? , parameter:tiger% 
這樣,保證了同樣的查詢、同樣的引數等條件下具有一樣的key。 
現在說說快取的value,如果是list方式的話,value在這裡並不是整個結果集,而是查詢出來的這一串ID。也就是說,不管是list方法還是iterate方法,第一次查詢的時候,它們的查詢方式很它們平時的方式是一樣的,list執行一條sql,iterate執行1+N條,多出來的行為是它們填充了快取。但是到同樣條件第二次查詢的時候,就都和iterate的行為一樣了,根據快取的key去快取裡面查到了value,value是一串id,然後在到class的快取裡面去一個一個的load出來。這樣做是為了節約記憶體。 
可以看出來,查詢快取需要開啟相關類的class快取。list和iterate方法第一次執行的時候,都是既填充查詢快取又填充class快取的。 
這裡還有一個很容易被忽視的重要問題,即開啟查詢快取以後,即使是list方法也可能遇到1+N的問題!相同條件第一次list的時候,因為查詢快取中找不到,不管class快取是否存在資料,總是傳送一條sql語句到資料庫獲取全部資料,然後填充查詢快取和class快取。但是第二次執行的時候,問題就來了,如果你的class快取的超時時間比較短,現在class快取都超時了,但是查詢快取還在,那麼list方法在獲取id串以後,將會一個一個去資料庫load!因此,class快取的超時時間一定不能短於查詢快取設定的超時時間!如果還設定了發呆時間的話,保證class快取的發呆時間也大於查詢的快取的生存時間。這裡還有其他情況,比如class快取被程式強制evict了,這種情況就請自己注意了。 

另外,如果hql查詢包含select字句,那麼查詢快取裡面的value就是整個結果集了。 

當hibernate更新資料庫的時候,它怎麼知道更新哪些查詢快取呢? 
hibernate在一個地方維護每個表的最後更新時間,其實也就是放在上面net.sf.hibernate.cache.UpdateTimestampsCache所指定的快取配置裡面。 
當通過hibernate更新的時候,hibernate會知道這次更新影響了哪些表。然後它更新這些表的最後更新時間。每個快取都有一個生成時間和這個快取所查詢的表,當hibernate查詢一個快取是否存在的時候,如果快取存在,它還要取出快取的生成時間和這個快取所查詢的表,然後去查詢這些表的最後更新時間,如果有一個表在生成時間後更新過了,那麼這個快取是無效的。 
可以看出,只要更新過一個表,那麼凡是涉及到這個表的查詢快取就失效了,因此查詢快取的命中率可能會比較低。 

Collection快取
需要在hbm的collection裡面設定 
<cache usage="read-write"/> 
假如class是Cat,collection叫children,那麼ehcache裡面配置 
<cache name="com.xxx.pojo.Cat.children" 
   maxElementsInMemory="20" eternal="false" timeToIdleSeconds="3600" timeToLiveSeconds="7200" 
   overflowToDisk="true" /> 
Collection的快取和前面查詢快取的list一樣,也是隻保持一串id,但它不會因為這個表更新過就失效,一個collection快取僅在這個collection裡面的元素有增刪時才失效。 
這樣有一個問題,如果你的collection是根據某個欄位排序的,當其中一個元素更新了該欄位時,導致順序改變時,collection快取裡面的順序沒有做更新。 

快取策略
只讀快取(read-only):沒有什麼好說的 
讀/寫快取(read-write):程式可能要的更新資料 
不嚴格的讀/寫快取(nonstrict-read-write):需要更新資料,但是兩個事務更新同一條記錄的可能性很小,效能比讀寫快取好 
事務快取(transactional):快取支援事務,發生異常的時候,快取也能夠回滾,只支援jta環境,這個我沒有怎麼研究過 

讀寫快取和不嚴格讀寫快取在實現上的區別在於,讀寫快取更新快取的時候會把快取裡面的資料換成一個鎖,其他事務如果去取相應的快取資料,發現被鎖住了,然後就直接取資料庫查詢。 
在hibernate2.1的ehcache實現中,如果鎖住部分快取的事務發生了異常,那麼快取會一直被鎖住,直到60秒後超時。 
不嚴格讀寫快取不鎖定快取中的資料。 


使用二級快取的前置條件
你的hibernate程式對資料庫有獨佔的寫訪問權,其他的程序更新了資料庫,hibernate是不可能知道的。你操作資料庫必需直接通過hibernate,如果你呼叫儲存過程,或者自己使用jdbc更新資料庫,hibernate也是不知道的。hibernate3.0的大批量更新和刪除是不更新二級快取的,但是據說3.1已經解決了這個問題。 
這個限制相當的棘手,有時候hibernate做批量更新、刪除很慢,但是你卻不能自己寫jdbc來優化,很鬱悶吧。 
SessionFactory也提供了移除快取的方法,你一定要自己寫一些JDBC的話,可以呼叫這些方法移除快取,這些方法是: 
void evict(Class persistentClass) 
          Evict all entries from the second-level cache. 
void evict(Class persistentClass, Serializable id) 
          Evict an entry from the second-level cache. 
void evictCollection(String roleName) 
          Evict all entries from the second-level cache. 
void evictCollection(String roleName, Serializable id) 
          Evict an entry from the second-level cache. 
void evictQueries() 
          Evict any query result sets cached in the default query cache region. 
void evictQueries(String cacheRegion) 
          Evict any query result sets cached in the named query cache region. 
不過我不建議這樣做,因為這樣很難維護。比如你現在用JDBC批量更新了某個表,有3個查詢快取會用到這個表,用evictQueries(String cacheRegion)移除了3個查詢快取,然後用evict(Class persistentClass)移除了class快取,看上去好像完整了。不過哪天你添加了一個相關查詢快取,可能會忘記更新這裡的移除程式碼。如果你的jdbc程式碼到處都是,在你新增一個查詢快取的時候,還知道其他什麼地方也要做相應的改動嗎? 

---------------------------------------------------- 

總結:
不要想當然的以為快取一定能提高效能,僅僅在你能夠駕馭它並且條件合適的情況下才是這樣的。hibernate的二級快取限制還是比較多的,不方便用jdbc可能會大大的降低更新效能。在不瞭解原理的情況下亂用,可能會有1+N的問題。不當的使用還可能導致讀出髒資料。 
如果受不了hibernate的諸多限制,那麼還是自己在應用程式的層面上做快取吧。 
在越高的層面上做快取,效果就會越好。就好像儘管磁碟有快取,資料庫還是要實現自己的快取,儘管資料庫有快取,咱們的應用程式還是要做快取。因為底層的快取它並不知道高層要用這些資料幹什麼,只能做的比較通用,而高層可以有針對性的實現快取,所以在更高的級別上做快取,效果也要好些吧。 


終於寫完了,好累……

相關推薦

最好hibernate二級快取解析

很多人對二級快取都不太瞭解,或者是有錯誤的認識,我一直想寫一篇文章介紹一下hibernate的二級快取的,今天終於忍不住了。 我的經驗主要來自hibernate2.1版本,基本原理和3.0、3.1是一樣的,請原諒我的頑固不化。hibernate的session提供了一級快取

應該是最好的3D機房監控系統了!

  3D機房視覺化技術將多種管理系統的複雜資訊融匯在虛擬模擬環境之中,以符合人類直覺的方式自然呈現,從而大大提升了資訊互動的效率,降低了資訊損耗和時間損耗,確保資訊傳遞的準確性和及時性,降低了資訊查詢和瀏覽的難度,使運維管理人員能夠大幅提升操控效率,加快響應速度,縮短處理時間。  機房不再需要現實中用腳走過去

普通人如何站在時代風口學好AI?最好的答案

摘要:當前,資料、演算法、算力的發展突破正推動AI應用的逐步落地。 AI是什麼? 根據維基百科的定義,人工智慧是一種新的通用目的技術(GPT, General Purpose Technology),它橫跨整個人類經濟的多種用途,具有巨大技術性互補和溢位效應。 簡而言之,AI是21世紀的一種基礎技術,它會應用

33歲學做軟體測試還來得及?最好的答案!

xinyi在某乎上看到一個問題,《33歲學軟體測試來得及嗎?》 問題:之前一直在工廠上班,看不到希望。已經33歲了,想轉學軟體測試來得及嗎? 同樣型別的問題,還有“我是大專學歷,學軟體測試能找到工作不?”“我是女生,軟體測試學起來難麼?”來咕泡的新學員們,每天都會遇到很多同學有這些

為什麼要求企業做高新技術認定?最好的答案!

高新技術企業是發展高新技術產業的重要基礎,是調整產業結構、提高國家競爭力的生力軍,在我國經濟發展中佔有十分重要的戰略地位。十幾年來,高新技術企業一直受到各級政府的高度重視,國家和地方主要採取稅收減免、股權激勵、科技計劃、專案用地、金融保險、出口信貸等多種政策措施,鼓勵和支援高新技術企業發展。我國已初步形成了培

為什麽要求企業做高新技術認定?最好的答案!

形象 策劃 什麽 strong 不可 領域 blog 初步 額的 高新技術企業是發展高新技術產業的重要基礎,是調整產業結構、提高國家競爭力的生力軍,在我國經濟發展中占有十分重要的戰略地位。十幾年來,高新技術企業一直受到各級政府的高度重視,國家和地方主要采取稅收減免、股權激勵

講神經網路最明白的一篇

%讀取訓練資料[f1,f2,f3,f4,class] = textread('trainData.txt' , '%f%f%f%f%f',150);%特徵值歸一化[input,minI,maxI] = premnmx( [f1 , f2 , f3 , f4 ]') ;%構造輸出矩陣s = length( c

紀錄一下的那些書籍

         古語云:溫故而知新.可謂道出學習的真諦.雖然讀了很多書,寫了很多部落格,也不敢說都已經會了,只能說當時會了.而作為學習閉環的最後一個過程.溫故是十分重要,故而寫此篇博文,紀錄那些我讀過的書籍,忘有空

visual assist x破解版的使用,這個是的裡面能用的唯一一個

visual assist x破解版:網盤連結: 破解教程: 1、軟體下載解壓完成後,點選VA_X_Setup2223開啟軟體安裝包,軟體會自動識別你電腦中的Microsoft Visual Studio版本,點選Install開始安裝軟體。 2、安裝完成。I

的遊戲開發書籍

編碼習慣及設計基礎 程式設計師修煉之道 這本書講解的一些設計原理很實用, 對設計感興趣的同學可以一看 推薦指數: 5星 圖形渲染, 客戶端 3D繪圖程式設計 推薦指數: 3星 Unity3D遊戲開發 雨鬆MOMO的早期Unity3D教學, 和他的網站一樣耐看 推薦指數: 4星 3D數學基礎:圖

得最易懂的一段AOP的解釋

面向切面程式設計(AOP是Aspect Oriented Program的首字母縮寫) 我們知道,面向物件的特點是繼承、多型和封裝。而封裝就要求將功能分散到不同的物件中去,這在軟體設計中往往稱為職責分配。實際上也就是說,讓不同的類設計不同的方法。

的比較好的視訊教程

1、CCNA紅頭髮主講視訊教程   1、在偏向理論的書籍當中很少重點談認證PPP協議,而在工程實踐中PPP協議非常重要,主要用於撥號,收費(如果沒有收費,運營商怎麼盈利,哈哈)。   2、在偏向理論的書籍中很少重點談ACL(access control list)過濾,

Eclipse的啟動優化詳解(的最全的優化), 終於解決啟動/編譯/執行期間的頻繁卡死問題了...

首先了解下JVM中幾個相關的概念: Xms:最小堆大小 Xmx:最大堆大小 Xmn:年輕代堆大小 Xss:每個執行緒的堆大小 PermSize:初始持久代大小 MaxPermSize:最大持久代大小 一般Xms、Xmx設定相同,PermSize、MaxPermSize設定

Hibernate 二級快取和查詢快取

一級快取:     1,在session上面有一個一級快取;一級快取的生命週期和session相同,一級快取最大生命週期就是一個執行緒;在web環境下面,session的最大生命週期就是一次請求;     2,一級快取可以用來幹嘛? &nb

Hibernate 二級快取的作用

使用快取,是需要對應用系統進行效能優化而常採用的一種重要手段。合理地運用快取,可以極大的提高應用系統的執行效率。 Hibernate中應用快取:因為應用程式訪問資料庫,讀寫資料的代價非常高,而利用持久層的快取可以減少應用程式與資料庫之間的互動,即把訪問過的資料儲存到快取中,應用程式再次訪問已經訪

Hibernate二級快取問題

相關概念和定義1、快取的意義把一些不常修改,但是又經常用的資料存放到記憶體中,這樣能減少與資料庫的互動,提升程式的效能 2、Hibernate中提供了兩級快取:第一級別的快取是Session級別的快取(比如說在呼叫get方法的時候,如果已經查詢過一次了,第二次就不會查了,而是直接返回session快取中已經

hibernate 二級快取和事務級別詳講

一、概述 這章總的分兩大塊來講解   第一大塊,hibernate的事務管理。對於hibernate的事務管理來說,如果之前學過資料庫的事務管理,那麼在這裡就順風順水了。如果沒學過,第一次遇到,那也沒關係,我會詳細解釋其中的內容。   第二大塊,hibernate的二級快取機制。這個看起

spring boot整合ehcache 2.x 用於hibernate二級快取

spring boot整合ehcache 2x 用於hibernate二級快取 專案依賴 Ehcache簡介 hibernate二級快取配置 ehcache配置檔案 ehcache事件監聽 註解方式使用二級快取 完整程式碼 本文將介紹如何在spring boot中整合ehcache作為hiberna

關於hibernate 二級快取 報錯問題

Maven   hibernate-ehcache 和hibernate-core版本需要一致 <dependency> <groupId>org.hibernate</groupId> <artifactId>h

Hibernate二級快取

Hibernate中沒有自己去實現二級快取,而是利用第三方的。簡單敘述一下配置過程,也作為自己以後用到的時候配置的一個參考。 1、我們需要加入額外的二級快取包,例如EHcache,將其包匯入。需要:ehcache-core-2.4.3.jar , hibernate-e