當前位置:成語大全網 - 書法字典 - 休眠緩存的詳細配置

休眠緩存的詳細配置

很多人對二級緩存不太了解,或者有錯誤的理解。我壹直想寫壹篇關於hibernate的二級緩存的文章,但今天我終於忍不住了。

我的經驗主要來自於hibernate2.1,基本原理與3.0和3.1相同。請原諒我的固執。

Hibernate的會話提供了壹級緩存。在每個會話中,同壹個id被加載兩次,並且兩個sql不會被發送到數據庫。但是,當會話關閉時,壹級緩存將無效。

二級緩存是會話工廠級別的全局緩存。它可以使用不同的緩存類庫,如ehcache、oscache等。,並且需要設置hibernate.cache.provider_class。我們在這裏使用ehcache,它在2.1中。

hibernate . cache . provider _ class = net . SF . hibernate . cache . ehcacheprovider

如果使用查詢緩存,加上

hibernate . cache . use _ query _ cache = true

緩存可以簡單地看作壹個映射,通過鍵可以在緩存中找到值。

類的緩存

對於壹個記錄,即壹個PO,它是根據ID找到的,緩存的鍵是ID,值是POJO。無論是列表、加載還是叠代,只要讀取壹個對象,就會填滿緩存。但是,list不會使用緩存,iterate將首先獲取數據庫選擇id,然後壹次加載壹個id。如果緩存中有壹個,它將從緩存中獲取它,否則它將進入數據庫加載。假設它是讀寫緩存,您需要設置:

& lt緩存使用=“讀寫“/& gt;

如果您使用的二級緩存實現是ehcache,則需要配置ehcache.xml。

& ltcache name =“com . XXX . POJO . foo“maxElementsInMemory =“500“eternal =“false“timeToLiveSeconds =“7200“timeToIdleSeconds =“3600“overflow todisk =“true“/& gt;

Eternal表示緩存是否永遠不會超時,timeToLiveSeconds是緩存中每個元素(這裏是壹個POJO)的超時時間。如果eternal =“false“,則在超過指定時間時將移除元素。TimeToIdleSeconds是發呆時間,是可選的。當緩存中的put元素超過500個時,如果overflow disk =“true“,緩存中的壹些數據將保存在硬盤上的臨時文件中。

每個需要緩存的類都應該這樣配置。如果妳沒有配置,hibernate會在啟動時警告妳,然後使用defaultCache的配置,這樣多個類將* * *享受壹個配置。

當壹個ID被hibernate修改時,hibernate會知道並刪除緩存。

這樣,您可能會認為相同的查詢條件,第壹個list,第二個iterate,您可以使用緩存。其實這個很難,因為妳無法判斷什麽時候是第壹次,而且每次查詢的條件通常都不壹樣。如果數據庫中有100條記錄,則id的範圍從1到100,第壹次列出前50個ID,但第二次叠代時查詢30-70的ID,然後是30-50。所以我壹直覺得叠代沒用,總會有1+N個問題。

(題外話:據說帶列表的大規模查詢會將整個結果集加載到內存中,非常慢,而iterate只選擇id,但大規模查詢總是需要分頁的,沒有人會真正加載整個結果集。如果壹個頁面上有20篇文章,iterate***需要執行21條語句。雖然list選擇了幾個字段,但它比iterate中的第壹個select id語句慢,但只有壹個語句。不需要加載整個結果集,hibernate也會根據數據庫方言進行優化,比如使用mysql的限制。總的來說,應該比榜單快。)

如果您想緩存列表或叠代查詢的結果,則需要使用查詢緩存。

查詢緩存

您需要首先配置hibernate . cache . use _ query _ cache = true。

如果您使用ehcache並配置ehcache.xml,請註意hibernate3.0不再是net.sf的包名

& lt緩存名稱=“net . SF . hibernate . cache . standardquerycache“

maxElementsInMemory =“50“eternal =“false“timeToIdleSeconds =“3600“

timeToLiveSeconds =“7200“overflow todisk =“true“/& gt。

& lt緩存名稱=“net . SF . hibernate . cache . updatetimestampscache“

maxElementsInMemory =“5000“eternal =“true“overflow todisk =“true“/& gt;

然後

query . set cacheable(true);//激活查詢緩存

query . setcache region(“mycache region“);//指定要使用的緩存,這是可選的。

第二行指定要使用的緩存是cacheRegion,也就是說,您可以為每個查詢緩存進行單獨的配置。為此,您需要在ehcache.xml中對其進行配置:

& ltcache name =“mycache region“maxElementsInMemory =“10“eternal =“false“timeToIdleSeconds =“3600“timeToLiveSeconds =“7200“overflow todisk =“true“/& gt;

如果省略第二行並且未設置cacheRegion,將使用上述標準查詢緩存的配置,即net。SF。冬眠。緩存。StandardQueryCache。

對於查詢緩存,緩存的鍵是根據hql生成的sql,加上參數、分頁等信息(可以通過日誌輸出看到,但其輸出不太可讀,最好更改其代碼)。

例如,hql:

c類的c.name是什麽?

大致如下生成sql:

從c類中選擇c.name?

參數是“tiger%”,所以查詢緩存的key*是這樣的字符串(我是靠內存寫的,不準確,但看完應該就明白了):

從c類中選擇c.name?,參數:tiger%

這樣,在相同的查詢、相同的參數和其他條件下保證了相同的密鑰。

現在讓我們談談緩存的值。如果是在列表模式下,這裏的值不是整個結果集,而是被查詢的ID。也就是說,無論是list方法還是iterate方法,第壹次查詢時,他們的查詢方法都與平時的方法相同。list執行壹個sql,iterate執行1+N,額外的行為是它們填滿了緩存。但是,當在相同條件下第二次進行查詢時,行為與iterate相同。根據緩存的鍵,在緩存中找到值,然後逐個加載到類的緩存中。這樣做是為了節省內存。

如您所見,查詢緩存需要打開相關類的類緩存。當第壹次執行list和iterate方法時,查詢緩存和類緩存都被填充。

還有壹個很容易被忽略的重要問題,就是即使是list方法在打開查詢緩存後也可能遇到1+N的問題!當第壹次列出相同的條件時,因為在查詢緩存中找不到它,所以無論類緩存中是否有數據,總是會向數據庫發送壹條sql語句來獲取所有數據,然後填充查詢緩存和類緩存。但是第二次執行時,問題就來了。如果妳的類緩存超時很短,現在類緩存超時了,但是查詢緩存還在,那麽list方法會在獲取id字符串後逐個進行數據庫加載!因此,類緩存的超時不得短於查詢緩存設置的超時!如果還設置了發呆時間,請確保類緩存的發呆時間也比查詢緩存的存活時間長。這裏還有其他情況,比如類緩存被程序強制驅逐,所以請自己註意這種情況。

此外,如果hql查詢包含select,那麽查詢緩存中的值就是整個結果集。

當hibernate更新數據庫時,它如何知道要更新哪些查詢緩存?

Hibernate在壹個地方維護每個表的最後更新時間,實際上是放在net指定的緩存配置中。SF。冬眠。緩存。更新上面的TimeStampsCache。

當通過hibernate更新時,hibernate將知道哪些表會受到此更新的影響。然後更新這些表的最後更新時間。每個緩存都有壹個生成時間和該緩存查詢的表。當hibernate查詢緩存是否存在時,它還會取出緩存的生成時間和該緩存查詢的表,然後查找這些表的最後更新時間。如果表在生成時間之後被更新,則該緩存無效。

可以看出,只要更新了壹個表,任何涉及該表的查詢緩存都將無效,因此查詢緩存的命中率可能很低。

集合緩存

需要在hbm的集合中設置。

& lt緩存使用=“讀寫“/& gt;

如果類是Cat並且集合是children,則ehcache中的配置

& ltcache name =“com . XXX . POJO . cat . children“

maxElementsInMemory =“20“eternal =“false“timeToIdleSeconds =“3600“timeToLiveSeconds =“7200“

overflow todisk =“true“/& gt。

集合的緩存與前面的查詢緩存的列表相同,但它只保留壹個id字符串,但它不會無效,因為該表已經更新。只有在添加或刪除集合中的元素時,集合緩存才會無效。

有壹個問題。如果您的集合是根據字段排序的,當其中壹個元素更新該字段時,順序會發生變化,並且集合緩存中的順序不會更新。

緩存策略

只讀緩存:沒什麽好說的。

讀寫緩存:更新程序可能需要的數據。

松散讀/寫緩存:數據需要更新,但兩個事務更新同壹條記錄的可能性很小,性能更好的是非嚴格讀寫緩存。

事務性緩存:緩存支持事務,當發生異常時,緩存也可以回滾,只支持jta環境,我對JTA環境沒有太多研究。

讀寫緩存和松散讀寫緩存的區別在於,當讀寫緩存更新緩存時,緩存中的數據將被鎖替換。如果其他事務獲取相應的緩存數據並發現它被鎖定,它們將直接獲取數據庫查詢。

在hibernate2.1中ehcache的實現中,如果鎖定部分緩存的事務發生異常,緩存將被鎖定,直到60秒後超時。

不嚴格讀寫緩存不會鎖定緩存中的數據。

使用L2緩存的前提條件

您的hibernate程序擁有對數據庫的獨占寫訪問權限,hibernate不可能知道其他進程已經更新了數據庫。您必須直接通過hibernate操作數據庫。如果您自己調用存儲過程或使用jdbc來更新數據庫,hibernate不知道。hibernate3.0的大規模更新和刪除不會更新二級緩存,但據說3.1已經解決了這個問題。

這個限制相當棘手。有時hibernate批量更新和刪除很慢,但妳不能自己編寫jdbc來優化它。很壓抑。

SessionFactory還提供了移除緩存的方法。如果您必須自己編寫壹些JDBC,可以調用這些方法來移除緩存。這些方法是:

void退出(類persistentClass)

從二級緩存中清除所有條目。

void退出(類persistentClass,可序列化id)

從二級高速緩存中逐出壹個條目。

void退出集合(字符串roleName)

從二級緩存中清除所有條目。

void退出集合(字符串角色名,可序列化id)

從二級高速緩存中逐出壹個條目。

void退出查詢()

清除默認查詢緩存區域中緩存的任何查詢結果集。

void退出查詢(字符串cacheRegion)

逐出在指定查詢緩存區域中緩存的任何查詢結果集。

但是我不推薦,因為很難維護。例如,如果使用JDBC批量更新壹個表,三個查詢緩存將使用該表,三個查詢緩存將使用evit查詢(字符串緩存)刪除,然後使用evit(Class persistent Class)刪除類緩存,這似乎是完整的。但是有壹天妳添加了壹個相關的查詢緩存,妳可能會忘記在這裏更新刪除代碼。如果您的jdbc代碼無處不在,那麽當您添加查詢緩存時,您知道還要在哪裏進行相應的更改嗎?

-

總結:

不要想當然地認為緩存壹定會提高性能,前提是您可以控制它並且條件合適。Hibernate有很多二級緩存限制,所以使用jdbc很不方便,這可能會大大降低更新性能。如果不了解原理就亂用,可能會出現1+N 0+N的問題,使用不當還可能導致讀取臟數據。

如果妳受不了hibernate的限制,妳應該在應用程序級別進行自己的緩存。

緩存級別越高,效果越好。這就像數據庫有自己的緩存,即使磁盤有緩存,我們的應用程序仍然必須緩存,即使數據庫有緩存。因為底層緩存不知道高層要對這些數據做什麽,所以只能是通用的,高層可以有針對性地實現緩存,所以在更高級別緩存會更好。