hibernate二级缓存作用、配置
緩存:緩存是什么,解決什么問題?
位于速度相差較大的兩種硬件/軟件之間的,用于協調兩者數據傳輸速度差異的結構,均可稱之為緩存Cache。緩存目的:讓數據更接近于應用程序,協調速度不匹配,使訪問速度更快。
緩存的范圍分為3類:
1.事務范圍(單Session即一級緩存)
事務范圍的緩存只能被當前事務訪問,每個事務都有各自的緩存,緩存內的數據通常采用相互關聯的對象形式.緩存的生命周期依賴于事務的生命周期,只有當事務結束時,緩存的生命周期才會結束.事務范圍的緩存使用內存作為存儲介質,一級緩存就屬于事務范圍.
2.應用范圍(單SessionFactory即二級緩存)
應用程序的緩存可以被應用范圍內的所有事務共享訪問.緩存的生命周期依賴于應用的生命周期,只有當應用結束時,緩存的生命周期才會結束.應用范圍的緩存可以使用內存或硬盤作為存儲介質,二級緩存就屬于應用范圍.
3.集群范圍(多SessionFactory)
在集群環境中,緩存被一個機器或多個機器的進程共享,緩存中的數據被復制到集群環境中的每個進程節點,進程間通過遠程通信來保證緩存中的數據的一致,緩存中的數據通常采用對象的松散數據形式.
什么是二級緩存?
二級緩存類似于一級緩存,可以緩存對象,但它是SessionFactory級別的緩存,由SessionFactory負責管理。因此,二級緩存的數據是session間共享的,不同的Session對象都可以共享二級緩存中的數據。
二級緩存適用場景:
1、對象數據頻繁共享
2、數據變化頻率底
二級緩存如何工作的:
Hibernate的二級緩存同一級緩存一樣,也是針對對象ID來進行緩存。所以說,二級緩存的作用范圍是針對根據ID獲得對象的查詢。
● 在執行各種條件查詢時,如果所獲得的結果集為實體對象的集合,那么就會把所有的數據對象根據ID放入到二級緩存中。
● 當Hibernate根據ID訪問數據對象的時候,首先會從Session一級緩存中查找,如果查不到并且配置了二級緩存,那么會從二級緩存中查找,如果還查不到,就會查詢數據庫,把結果按照ID放入到緩存中。
● 刪除、更新、增加數據的時候,同時更新緩存。
與Hibernate一級緩存Session范圍相對的是SessionFactory范圍的二級緩存,SessionFactory也提供了相應的緩存機制。SessionFactory緩存可以依據功能和目的的不同而劃分為內置緩存和外置緩存。
SessionFactory的內置緩存中存放了映射元數據和預定義SQL語句,映射元數據是映射文件中數據的副本,而預定義SQL語句是在Hibernate初始化階段根據映射元數據推導出來的。SessionFactory的內置緩存是只讀的,應用程序不能修改緩存中的映射元數據和預定義SQL語句,因此SessionFactory不需要進行內置緩存與映射文件的同步。
SessionFactory的外置緩存是一個可配置的插件。在默認情況下,SessionFactory不會啟用這個插件。外置緩存的數據是數據庫數據的副本,外置緩存的介質可以是內存或者硬盤。SessionFactory的外置緩存也被稱為Hibernate的二級緩存。
Hibernate的二級緩存的實現原理與一級緩存是一樣的,也是通過以ID為key的Map來實現對對象的緩存。
二級緩存是緩存實體對象的,由于Hibernate的二級緩存是作用在SessionFactory范圍內的,因而它比一級緩存的范圍更廣,可以被所有的Session對象所共享。
在通常情況下會將具有以下特征的數據放入到二級緩存中:
● 很少被修改的數據。
● 不是很重要的數據,允許出現偶爾并發的數據。
● 不會被并發訪問的數據。
● 常量數據。
● 不會被第三方修改的數據
而對于具有以下特征的數據則不適合放在二級緩存中:
● 經常被修改的數據。
● 財務數據,絕對不允許出現并發。
● 與其他應用共享的數據。
在這里特別要注意的是對放入緩存中的數據不能有第三方的應用對數據進行更改(其中也包括在自己程序中使用其他方式進行數據的修改,例如,JDBC),因為那樣Hibernate將不會知道數據已經被修改,也就無法保證緩存中的數據與數據庫中數據的一致性。
常見的緩存組件
在默認情況下,Hibernate會使用EHCache作為二級緩存組件。但是,可以通過設置hibernate.cache.provider_class屬性,指定其他的緩存策略,該緩存策略必須實現org.hibernate.cache.CacheProvider接口。
通過實現org.hibernate.cache.CacheProvider接口可以提供對不同二級緩存組件的支持,此接口充當緩存插件與Hibernate之間的適配器。
集群緩存的概念:
當一臺服務器上的執行了update方法修改了一條數據,那么只有這一臺服務器上的二級緩存會同步于數據庫,其他服務器上的二級緩存里面這條數據就沒意義了。這個時候用OSCache緩存機制,只要有一臺服務器上有數據修改了,馬上會從配置文件中找到配置好的其他服務器IP地址,進行廣播,告訴他們我這條數據修改了,你們也更新同步一下。
如何在項目里使用二級緩存:
首先在hibernate.cfg.xml開啟二級緩存
然后是ehcache配置(ehcache.xml)
cache參數詳解:
● name:指定區域名
● maxElementsInMemory :緩存在內存中的最大數目
● maxElementsOnDisk:緩存在磁盤上的最大數目
● eternal :設置是否永遠不過期
● overflowToDisk : 硬盤溢出數目
● timeToIdleSeconds :對象處于空閑狀態的最多秒數后銷毀
● timeToLiveSeconds :對象處于緩存狀態的最多秒數后銷毀
● memoryStoreEvictionPolicy:緩存算法,有LRU(默認)、LFU、LFU
關于緩存算法,常見有三種:
● LRU:(Least Rencently Used)新來的對象替換掉使用時間算最近很少使用的對象
● LFU:(Least Frequently Used)替換掉按命中率高低算比較低的對象
● LFU:(First In First Out)把最早進入二級緩存的對象替換掉
ehcache.xml代碼:
<?xml version="1.0" encoding="UTF-8"?> <ehcache> <!--如果緩存中的對象存儲超過指定的緩存數量的對象存儲的磁盤地址--> <diskStore path="D:/ehcache"/> <!-- 默認cache:如果沒有對應的特定區域的緩存,就使用默認緩存 --> <defaultCache maxElementsInMemory="10000" eternal="false" timeToIdleSeconds="300" timeToLiveSeconds="600" overflowToDisk="false"/> <!-- 指定區域cache:通過name指定,name對應到Hibernate中的區域名即可--> <cache name="cn.javass.h3test.model.UserModel" eternal="false" maxElementsInMemory="100" timeToIdleSeconds="1200" timeToLiveSeconds="1200" overflowToDisk="false"> </cache> </ehcache>在每個實體的hbm文件中配置cache元素,usage可以是read-only或者是read-write等4種。
<?xml version="1.0" encoding='UTF-8'?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" > <hibernate-mapping> <class> <!-- 設置該持久化類的二級緩存并發訪問策略 read-only read-write nonstrict-read-write transactional--> <class name="cn.java.test.model.User" table="TBL_USER"> <cache usage="read-write"/> ...... </class> </hibernate-mapping>也可以用Hibernate注解配置緩存實體類:
@Entity @Table @Cache(usage = CacheConcurrencyStrategy.READ_WRITE) public class User implements Serializable { private static final long serialVersionUID = -5121812640999313420L; private Integer id; private String name; ...... }Query或Criteria接口查詢時設置其setCacheable(true):
默認的如果不在程序中顯示的執行查詢緩存聲明操作,Hibernate是不會對查詢的list進行緩存的。
如果開啟了二級緩存,由于session是共享二級緩存的,只要緩存里面有要查詢的對象,就不會向數據庫發出sql,如果在二級緩存里沒有找到需要的數據就會發出sql語句去數據庫拿。
一級緩存的管理:
● evit(Object obj)將指定的持久化對象從一級緩存中清除,釋放對象所占用的內存資源,指定對象從持久化狀態變為脫管狀態,從而成為游離對象.
● clear()將一級緩存中的所有持久化對象清除,釋放其占用的內存資源
● contains(Object obj)判斷指定的對象是否存在于一級緩存中.
● flush()刷新一級緩存區的內容,使之與數據庫數據保持同步.
二級緩存的管理:
● evict(Class arg0, Serializable arg1)將某個類的指定ID的持久化對象從二級緩存中清除,釋放對象所占用的資源.
evict(Class arg0)將指定類的所有持久化對象從二級緩存中清除,釋放其占用的內存資源 :
sessionFactory.evict(Customer.class);evictCollection(String arg0)將指定類的所有持久化對象的指定集合從二級緩存中清除,釋放其占用的內存資源.
sessionFactory.evictCollection("Customer.orders");設置一級緩存和二級緩存的交互權限
session = HibernateUtils.getSession(); session.beginTransaction(); //僅向二級緩存讀數據,而不向二級緩存寫數據,這里load的數據就不會放入二級緩存,下次再查還是會去數據庫拿 session.setCacheMode(CacheMode.GET); //只向二級緩存寫數據,而不從二級緩存讀數據 //session.setCacheMode(CacheMode.PUT); //不與二級緩存交互 //session.setCacheMode(CacheMode.IGNORE); //可以與二級緩存交互 //session.setCacheMode(CacheMode.NORMAL); Student student = (Student)session.load(Student.class, 1); session.getTransaction().commit();設置二級緩存策略
● READ_ONLY:實體只讀緩存
只讀緩存不允許更新,將報錯Can’t write to a readonly object。
允許新增,(從2.0以后新增直接添加到二級緩存)
● NONSTRICT_READ_WRITE:實體非嚴格讀/寫緩存
允許更新,更新后緩存失效,需再查詢一次。
允許新增,新增記錄自動加到二級緩存中。
整個過程不加鎖。
● READ_WRITE:實體讀/寫緩存
允許更新,更新后自動同步到緩存。
允許新增,新增記錄后自動同步到緩存。
保證read committed隔離級別及可重復讀隔離級別(通過時間戳實現)
整個過程加鎖,如果當前事務的時間戳早于二級緩存中的條目的時間戳,說明該條目已經被別的
事務修改了,此時重新查詢一次數據庫,否則才使用緩存數據,因此保證可重復讀隔離級別。
讀寫緩存和不嚴格讀寫緩存在實現上的區別在于,讀寫緩存更新緩存的時候會把緩存里面的數據換成一個鎖
● TRANSACTIONAL:實體事務緩存
緩存支持事務,發生異常的時候,緩存也能夠回滾,只支持jta環境
● Collection集合緩存
和實體并發策略有相同含義;
但集合緩存只緩存集合元素的標識符,在二級緩存中只存放相應實體的標識符,然后再通過標識符去二級緩存查找相應的實體最后組合為集合返回
Collection的緩存和前面查詢緩存的list一樣,也是只保持一串id,但它不會因為這個表更新過就失效,一個collection緩存僅在這個collection里面的元素有增刪時才失效。
這樣有一個問題,如果你的collection是根據某個字段排序的,當其中一個元素更新了該字段時,導致順序改變時,collection緩存里面的順序沒有做更新 。
高速緩存區域
Hibernate在不同的高速緩存區域保存不同的類(實體)/集合,如果不配置區域默認都保存到“默認緩存”(defaultCache)中。
●每一個區域可以設置過期策略、緩存條目大小等等。
●對于類緩存,默認區域名是全限定類名,如cn.javass.h3test.model.UserModel。
●對于集合而言,默認區域名是全限定類名+屬性名,如cn.javass.….UserModel.farms。
●可通過hibernate.cache.region_prefix指定特定SessionFactory的區域前綴,如前綴是h3test,則如類緩存的區域名就是h3test.cn.javass.h3test.model.UserModel。如果應用程序使用多個SessionFactory這可能是必須的。
可通過自定義區域名,不過默認其實就可以了。
一些對二級緩存的理解
當hibernate更新數據庫的時候,它怎么知道更新哪些查詢緩存呢?
hibernate在一個地方維護每個表的最后更新時間,其實也就是放在上面net.sf.hibernate.cache.UpdateTimestampsCache所指定的緩存配置里面。
當通過hibernate更新的時候,hibernate會知道這次更新影響了哪些表。然后它更新這些表的最后更新時間。每個緩存都有一個生成時間和這個緩存所查詢的表,當hibernate查詢一個緩存是否存在的時候,如果緩存存在,它還要取出緩存的生成時間和這個緩存所查詢的表,然后去查找這些表的最后更新時間,如果有一個表在生成時間后更新過了,那么這個緩存是無效的。
如果找到的時間戳晚于高速緩存查詢結果的時間戳,那么緩存結果將被丟棄,重新執行一次查詢。
可以看出,只要更新過一個表,那么凡是涉及到這個表的查詢緩存就失效了,因此查詢緩存的命中率可能會比較低。
使用二級緩存的前置條件
對于那些查詢非常多但插入、刪除、更新非常少的應用程序來說,查詢緩存可提升性能。但寫入多查詢少的沒有用,總失效。
hibernate程序對數據庫有獨占的寫訪問權,其他的進程更新了數據庫,hibernate是不可能知道的。
你操作數據庫必需直接通過hibernate,如果你調用存儲過程,或者自己使用jdbc更新數據庫,hibernate也是不知道的。
這個限制相當的棘手,有時候hibernate做批量更新、刪除很慢,但是你卻不能自己寫jdbc來優化。
當然可以用SessionFactory提供的移除緩存的方法(上面的二級緩存的管理里面有介紹)
總結
不要想當然的以為緩存一定能提高性能,僅僅在你能夠駕馭它并且條件合適的情況下才是這樣的。hibernate的二級緩存限制還是比較多的,不方便用jdbc可能會大大的降低更新性能。在不了解原理的情況下亂用,可能會有1+N的問題。不當的使用還可能導致讀出臟數據。
如果受不了Hibernate的諸多限制,那么還是自己在應用程序的層面上做緩存吧!
在越高的層面上做緩存,效果就會越好。就好像盡管磁盤有緩存,數據庫還是要實現自己的緩存,盡管數據庫有緩存,咱們的應用程序還是要做緩存。因為底層的緩存它并不知道高層要用這些數據干什么,只能做的比較通用,而高層可以有針對性的實現緩存,所以在更高的級別上做緩存,效果也要好些吧!
總結
以上是生活随笔為你收集整理的hibernate二级缓存作用、配置的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java之final的各种用法
- 下一篇: 动态版简易通讯录制作