<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    posts - 73,  comments - 55,  trackbacks - 0
    ?很多人對二級緩存都不太了解,或者是有錯(cuò)誤的認(rèn)識,我一直想寫一篇文章介紹一下hibernate的二級緩存的,今天終于忍不住了。?
    我的經(jīng)驗(yàn)主要來自hibernate2.1版本,基本原理和3.0、3.1是一樣的,請?jiān)徫业念B固不化。?

    hibernate的session提供了一級緩存,每個(gè)session,對同一個(gè)id進(jìn)行兩次load,不會(huì)發(fā)送兩條sql給數(shù)據(jù)庫,但是session關(guān)閉的時(shí)候,一級緩存就失效了。?

    二級緩存是SessionFactory級別的全局緩存,它底下可以使用不同的緩存類庫,比如ehcache、oscache等,需要設(shè)置hibernate.cache.provider_class,我們這里用ehcache,在2.1中就是?
    hibernate.cache.provider_class=net.sf.hibernate.cache.EhCacheProvider?
    如果使用查詢緩存,加上?
    hibernate.cache.use_query_cache=true?


    緩存可以簡單的看成一個(gè)Map,通過key在緩存里面找value。?

    Class的緩存?
    對于一條記錄,也就是一個(gè)PO來說,是根據(jù)ID來找的,緩存的key就是ID,value是POJO。無論list,load還是iterate,只要讀出一個(gè)對象,都會(huì)填充緩存。但是list不會(huì)使用緩存,而iterate會(huì)先取數(shù)據(jù)庫select?id出來,然后一個(gè)id一個(gè)id的load,如果在緩存里面有,就從緩存取,沒有的話就去數(shù)據(jù)庫load。假設(shè)是讀寫緩存,需要設(shè)置:?
    ?
    如果你使用的二級緩存實(shí)現(xiàn)是ehcache的話,需要配置ehcache.xml?
    ?
    其中eternal表示緩存是不是永遠(yuǎn)不超時(shí),timeToLiveSeconds是緩存中每個(gè)元素(這里也就是一個(gè)POJO)的超時(shí)時(shí)間,如果 eternal="false",超過指定的時(shí)間,這個(gè)元素就被移走了。timeToIdleSeconds是發(fā)呆時(shí)間,是可選的。當(dāng)往緩存里面put的元素超過500個(gè)時(shí),如果overflowToDisk="true",就會(huì)把緩存中的部分?jǐn)?shù)據(jù)保存在硬盤上的臨時(shí)文件里面。?
    每個(gè)需要緩存的class都要這樣配置。如果你沒有配置,hibernate會(huì)在啟動(dòng)的時(shí)候警告你,然后使用defaultCache的配置,這樣多個(gè)class會(huì)共享一個(gè)配置。?
    當(dāng)某個(gè)ID通過hibernate修改時(shí),hibernate會(huì)知道,于是移除緩存。?
    這樣大家可能會(huì)想,同樣的查詢條件,第一次先list,第二次再iterate,就可以使用到緩存了。實(shí)際上這是很難的,因?yàn)槟銦o法判斷什么時(shí)候是第一次,而且每次查詢的條件通常是不一樣的,假如數(shù)據(jù)庫里面有100條記錄,id從1到100,第一次list的時(shí)候出了前50個(gè)id,第二次iterate的時(shí)候卻查詢到30至70號id,那么30-50是從緩存里面取的,51到70是從數(shù)據(jù)庫取的,共發(fā)送1+20條sql。所以我一直認(rèn)為iterate沒有什么用,總是會(huì)有1+N的問題。?
    (題外話:有說法說大型查詢用list會(huì)把整個(gè)結(jié)果集裝入內(nèi)存,很慢,而iterate只select?id比較好,但是大型查詢總是要分頁查的,誰也不會(huì)真的把整個(gè)結(jié)果集裝進(jìn)來,假如一頁20條的話,iterate共需要執(zhí)行21條語句,list雖然選擇若干字段,比iterate第一條select?id語句慢一些,但只有一條語句,不裝入整個(gè)結(jié)果集hibernate還會(huì)根據(jù)數(shù)據(jù)庫方言做優(yōu)化,比如使用 mysql的limit,整體看來應(yīng)該還是list快。)?
    如果想要對list或者iterate查詢的結(jié)果緩存,就要用到查詢緩存了?

    查詢緩存?
    首先需要配置hibernate.cache.use_query_cache=true?
    如果用ehcache,配置ehcache.xml,注意hibernate3.0以后不是net.sf的包名了?

    maxElementsInMemory="50"?eternal="false"?timeToIdleSeconds="3600"?
    timeToLiveSeconds="7200"?overflowToDisk="true"/>?

    maxElementsInMemory="5000"?eternal="true"?overflowToDisk="true"/>?
    然后?
    query.setCacheable(true);//激活查詢緩存?
    query.setCacheRegion("myCacheRegion");//指定要使用的cacheRegion,可選?
    第二行指定要使用的cacheRegion是myCacheRegion,即你可以給每個(gè)查詢緩存做一個(gè)單獨(dú)的配置,使用setCacheRegion來做這個(gè)指定,需要在ehcache.xml里面配置它:?
    ?
    如果省略第二行,不設(shè)置cacheRegion的話,那么會(huì)使用上面提到的標(biāo)準(zhǔn)查詢緩存的配置,也就是net.sf.hibernate.cache.StandardQueryCache?

    對于查詢緩存來說,緩存的key是根據(jù)hql生成的sql,再加上參數(shù),分頁等信息(可以通過日志輸出看到,不過它的輸出不是很可讀,最好改一下它的代碼)。?
    比如hql:?
    from?Cat?c?where?c.name?like???
    生成大致如下的sql:?
    select?*?from?cat?c?where?c.name?like???
    參數(shù)是"tiger%",那么查詢緩存的key*大約*是這樣的字符串(我是憑記憶寫的,并不精確,不過看了也該明白了):?
    select?*?from?cat?c?where?c.name?like???,?parameter:tiger%?
    這樣,保證了同樣的查詢、同樣的參數(shù)等條件下具有一樣的key。?
    現(xiàn)在說說緩存的value,如果是list方式的話,value在這里并不是整個(gè)結(jié)果集,而是查詢出來的這一串ID。也就是說,不管是list方法還是 iterate方法,第一次查詢的時(shí)候,它們的查詢方式很它們平時(shí)的方式是一樣的,list執(zhí)行一條sql,iterate執(zhí)行1+N條,多出來的行為是它們填充了緩存。但是到同樣條件第二次查詢的時(shí)候,就都和iterate的行為一樣了,根據(jù)緩存的key去緩存里面查到了value,value是一串 id,然后在到class的緩存里面去一個(gè)一個(gè)的load出來。這樣做是為了節(jié)約內(nèi)存。?
    可以看出來,查詢緩存需要打開相關(guān)類的class緩存。list和iterate方法第一次執(zhí)行的時(shí)候,都是既填充查詢緩存又填充class緩存的。?
    這里還有一個(gè)很容易被忽視的重要問題,即打開查詢緩存以后,即使是list方法也可能遇到1+N的問題!相同條件第一次list的時(shí)候,因?yàn)椴樵兙彺嬷姓也坏剑还躢lass緩存是否存在數(shù)據(jù),總是發(fā)送一條sql語句到數(shù)據(jù)庫獲取全部數(shù)據(jù),然后填充查詢緩存和class緩存。但是第二次執(zhí)行的時(shí)候,問題就來了,如果你的class緩存的超時(shí)時(shí)間比較短,現(xiàn)在class緩存都超時(shí)了,但是查詢緩存還在,那么list方法在獲取id串以后,將會(huì)一個(gè)一個(gè)去數(shù)據(jù)庫 load!因此,class緩存的超時(shí)時(shí)間一定不能短于查詢緩存設(shè)置的超時(shí)時(shí)間!如果還設(shè)置了發(fā)呆時(shí)間的話,保證class緩存的發(fā)呆時(shí)間也大于查詢的緩存的生存時(shí)間。這里還有其他情況,比如class緩存被程序強(qiáng)制evict了,這種情況就請自己注意了。?

    另外,如果hql查詢包含select字句,那么查詢緩存里面的value就是整個(gè)結(jié)果集了。?

    當(dāng)hibernate更新數(shù)據(jù)庫的時(shí)候,它怎么知道更新哪些查詢緩存呢??
    hibernate在一個(gè)地方維護(hù)每個(gè)表的最后更新時(shí)間,其實(shí)也就是放在上面net.sf.hibernate.cache.UpdateTimestampsCache所指定的緩存配置里面。?
    當(dāng)通過hibernate更新的時(shí)候,hibernate會(huì)知道這次更新影響了哪些表。然后它更新這些表的最后更新時(shí)間。每個(gè)緩存都有一個(gè)生成時(shí)間和這個(gè)緩存所查詢的表,當(dāng)hibernate查詢一個(gè)緩存是否存在的時(shí)候,如果緩存存在,它還要取出緩存的生成時(shí)間和這個(gè)緩存所查詢的表,然后去查找這些表的最后更新時(shí)間,如果有一個(gè)表在生成時(shí)間后更新過了,那么這個(gè)緩存是無效的。?
    可以看出,只要更新過一個(gè)表,那么凡是涉及到這個(gè)表的查詢緩存就失效了,因此查詢緩存的命中率可能會(huì)比較低。?

    Collection緩存?
    需要在hbm的collection里面設(shè)置?
    ?
    假如class是Cat,collection叫children,那么ehcache里面配置?

    maxElementsInMemory="20"?eternal="false"?timeToIdleSeconds="3600"?timeToLiveSeconds="7200"?
    overflowToDisk="true"?/>?
    Collection的緩存和前面查詢緩存的list一樣,也是只保持一串id,但它不會(huì)因?yàn)檫@個(gè)表更新過就失效,一個(gè)collection緩存僅在這個(gè)collection里面的元素有增刪時(shí)才失效。?
    這樣有一個(gè)問題,如果你的collection是根據(jù)某個(gè)字段排序的,當(dāng)其中一個(gè)元素更新了該字段時(shí),導(dǎo)致順序改變時(shí),collection緩存里面的順序沒有做更新。?

    緩存策略?
    只讀緩存(read-only):沒有什么好說的?
    讀/寫緩存(read-write):程序可能要的更新數(shù)據(jù)?
    不嚴(yán)格的讀/寫緩存(nonstrict-read-write):需要更新數(shù)據(jù),但是兩個(gè)事務(wù)更新同一條記錄的可能性很小,性能比讀寫緩存好?
    事務(wù)緩存(transactional):緩存支持事務(wù),發(fā)生異常的時(shí)候,緩存也能夠回滾,只支持jta環(huán)境,這個(gè)我沒有怎么研究過?

    讀寫緩存和不嚴(yán)格讀寫緩存在實(shí)現(xiàn)上的區(qū)別在于,讀寫緩存更新緩存的時(shí)候會(huì)把緩存里面的數(shù)據(jù)換成一個(gè)鎖,其他事務(wù)如果去取相應(yīng)的緩存數(shù)據(jù),發(fā)現(xiàn)被鎖住了,然后就直接取數(shù)據(jù)庫查詢。?
    在hibernate2.1的ehcache實(shí)現(xiàn)中,如果鎖住部分緩存的事務(wù)發(fā)生了異常,那么緩存會(huì)一直被鎖住,直到60秒后超時(shí)。?
    不嚴(yán)格讀寫緩存不鎖定緩存中的數(shù)據(jù)。?


    使用二級緩存的前置條件?
    你的hibernate程序?qū)?shù)據(jù)庫有獨(dú)占的寫訪問權(quán),其他的進(jìn)程更新了數(shù)據(jù)庫,hibernate是不可能知道的。你操作數(shù)據(jù)庫必需直接通過 hibernate,如果你調(diào)用存儲過程,或者自己使用jdbc更新數(shù)據(jù)庫,hibernate也是不知道的。hibernate3.0的大批量更新和刪除是不更新二級緩存的,但是據(jù)說3.1已經(jīng)解決了這個(gè)問題。?
    這個(gè)限制相當(dāng)?shù)募郑袝r(shí)候hibernate做批量更新、刪除很慢,但是你卻不能自己寫jdbc來優(yōu)化,很郁悶吧。?
    SessionFactory也提供了移除緩存的方法,你一定要自己寫一些JDBC的話,可以調(diào)用這些方法移除緩存,這些方法是:?
    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.?
    不過我不建議這樣做,因?yàn)檫@樣很難維護(hù)。比如你現(xiàn)在用JDBC批量更新了某個(gè)表,有3個(gè)查詢緩存會(huì)用到這個(gè)表,用evictQueries (String?cacheRegion)移除了3個(gè)查詢緩存,然后用evict(Class?persistentClass)移除了class緩存,看上去好像完整了。不過哪天你添加了一個(gè)相關(guān)查詢緩存,可能會(huì)忘記更新這里的移除代碼。如果你的jdbc代碼到處都是,在你添加一個(gè)查詢緩存的時(shí)候,還知道其他什么地方也要做相應(yīng)的改動(dòng)嗎??

    ----------------------------------------------------?

    總結(jié):?
    不要想當(dāng)然的以為緩存一定能提高性能,僅僅在你能夠駕馭它并且條件合適的情況下才是這樣的。hibernate的二級緩存限制還是比較多的,不方便用jdbc可能會(huì)大大的降低更新性能。在不了解原理的情況下亂用,可能會(huì)有1+N的問題。不當(dāng)?shù)氖褂眠€可能導(dǎo)致讀出臟數(shù)據(jù)。?
    如果受不了hibernate的諸多限制,那么還是自己在應(yīng)用程序的層面上做緩存吧。?
    在越高的層面上做緩存,效果就會(huì)越好。就好像盡管磁盤有緩存,數(shù)據(jù)庫還是要實(shí)現(xiàn)自己的緩存,盡管數(shù)據(jù)庫有緩存,咱們的應(yīng)用程序還是要做緩存。因?yàn)榈讓拥木彺嫠⒉恢栏邔右眠@些數(shù)據(jù)干什么,只能做的比較通用,而高層可以有針對性的實(shí)現(xiàn)緩存,所以在更高的級別上做緩存,效果也要好些吧。?
    posted on 2006-09-27 13:39 保爾任 閱讀(465) 評論(0)  編輯  收藏

    只有注冊用戶登錄后才能發(fā)表評論。


    網(wǎng)站導(dǎo)航:
     

    <2025年5月>
    27282930123
    45678910
    11121314151617
    18192021222324
    25262728293031
    1234567

    常用鏈接

    留言簿(4)

    隨筆分類

    隨筆檔案

    文章分類

    文章檔案

    搜索

    •  

    最新評論

    閱讀排行榜

    評論排行榜

    主站蜘蛛池模板: 国产成人A人亚洲精品无码| 亚洲欧好州第一的日产suv| 91精品导航在线网址免费| 亚洲另类精品xxxx人妖| 国产高清视频在线免费观看| 免费精品国自产拍在线播放| 久久99国产亚洲高清观看首页| 很黄很黄的网站免费的| 国产精品久久久久久亚洲小说| 狠狠色伊人亚洲综合成人| 九九精品免费视频| 一级毛片视频免费观看| 亚洲手机中文字幕| 4338×亚洲全国最大色成网站| 3d成人免费动漫在线观看| 在线观看亚洲网站| 亚洲色图视频在线观看| 全黄性性激高免费视频| 91精品啪在线观看国产线免费| 免费国产va视频永久在线观看| 亚洲天天在线日亚洲洲精| 国产传媒在线观看视频免费观看| 久久99精品国产免费观看| 偷自拍亚洲视频在线观看| 精品亚洲aⅴ在线观看| 亚洲国产精品专区在线观看| 国产91色综合久久免费分享| 亚洲黄片手机免费观看| 亚洲国产精品成人综合色在线| 亚洲av色福利天堂| 亚洲午夜精品久久久久久浪潮| 国内免费高清在线观看| 青青草无码免费一二三区| 人禽伦免费交视频播放| 亚洲狠狠色丁香婷婷综合| 久久久久亚洲Av无码专| 毛茸茸bbw亚洲人| 免费国产综合视频在线看| 男人的好看免费观看在线视频| 免费国产污网站在线观看15| 黄色毛片免费网站|