hibernatesession緩存
⑴ hibernate session緩存疑問
我現在也在看這本書,"對象關聯在session上"和"對象保存在session緩存中"是一個概念。
在session中delete(a)時,如果a是持久化對象(已經在session的緩存中),session就執行delete語句,刪除資料庫中對應的記錄。
如果a是游離對象,先使游離對象a和session關聯(其實就是載入到session的緩存中),使它變成一個持久化對象,然後在執行delete語句。
session的增刪改操作是面向對象的,你仔細研究研究 第7章 操縱持久化對象 看看session是怎麼把臨時對象和游離對象關聯到session緩存中的;
怎麼把持久化對象轉變為游離對象或臨時對象的!
⑵ Hibernate中Session什麼意思
Hibernate中Session 是Hibernate中的緩存對象 用此session操作資料庫後會緩存返回的結果在session裡面 當你再次操作資料庫的時候 如果session緩存裡面有相應的值 則不用去與資料庫交互直接返回結果
servlet 中的Session 也是緩存 其緩存你與伺服器對話時候的一些信息
總之所有的session基本都是起緩存作用的 就是把一些信息緩存在內存中 方便存取值
但是不同的session是不能相互直接賦值的 因為是兩個不同的概念 只是名字一樣
名字一樣也是為了幫助程序員理解 學了servlet的session 後再遇到session也就應該是緩存作用的
不知道我的回答你滿意不
⑶ hibernate session和cookie的區別
Session.load/get方法均可以根據指定的實體類和id從資料庫讀取記錄,並返回與之對應的實體對象。
其區別在於:
如果未能發現符合條件的記錄,get方法返回null,而load方法會拋出一個ObjectNotFoundException。
Load方法可返回實體的代理類實例,而get方法永遠直接返回實體類。
load方法可以充分利用內部緩存和二級緩存中的現有數據,而get方法則僅僅在內部緩存中進行數據查找,如沒有發現對應數據,將越過二級緩存,直接調用sql完成數據讀取。
Session在載入實體對象時,將經過的過程:
首先,Hibernate中維持了兩級緩存。第一級緩存由Session實例維護,其中保持了Session當前所有關聯實體的數據,也稱為內部緩存。而第二級緩存則存在於SessionFactory層次,由當前所有由本SessionFactory構造的Session實例共享。出於性能考慮,避免無謂的資料庫訪問,Session在調用資料庫查詢功能之前,會先在緩存中進行查詢。首先在第一級緩存中,通過實體類型和id進行查找,如果第一級緩存查找命中,且數據狀態合法,則直接返回。
之後,Session會在當前「NonExists(把無效的條件寫成一個黑名單,既然無效,那麼也沒必要再查下去)」記錄中進行查找,如果「NonExists」記錄中存在同樣的查詢條件,則返回null。「NonExists」記錄了當前Session實例在之前所有查詢操作中,未能查詢到有效數據的查詢條件(相當於一個查詢黑名單列表)。如此一來,如果Session中一個無效的查詢條件重復出現,即可迅速作出判斷,從而獲得最佳的性能表現。
對於load方法而言,如果內部緩存中未發現有效數據,則查詢第二級緩存,如果第二級緩存命中,則返回。
如在緩存中未發現有效數據,則發起資料庫查詢操作(Select SQL),如經過查詢未發現對應記錄,則將此次查詢的信息在「NonExists」中加以記錄,並返回null。
根據映射配置和Select SQL得到的ResultSet,創建對應的數據對象。
將其數據對象納入當前Session實體管理容器(一級緩存)。
執行Interceptor.onLoad方法(如果有對應的Interceptor)。
將數據對象納入二級緩存。
如果數據對象實現了LifeCycle介面,則調用數據對象的onLoad方法。
返回數據對象。
/** *//**
* load()方法的執行順序如下:
* a):首先通過id在session緩存中查找對象,如果存在此id的對象,直接將其返回
* b):在二級緩存中查找,找到後將 其返回。
* c):如果在session緩存和二級緩存中都找不到此對象,則從資料庫中載入有此ID的對象
* 因此load()方法並不總是導致SQL語句,只有緩存中無此數據時,才向資料庫發送SQL!
*/
/** *//**
* 與get()的區別:
* 1:在立即載入對象(當hibernate在從資料庫中取得數據組裝好一個對象後
* 會立即再從資料庫取得數據此對象所關聯的對象)時,如果對象存在,
* load()和get()方法沒有區別,都可以取得已初始化的對象;但如果當對
* 象不存在且是立即載入時,使用get()方法則返回null,而使用load()則
* 拋出一個異常。因此使用load()方法時,要確認查詢的主鍵ID一定是存在
* 的,從這一點講它沒有get方便!
* 2:在延遲載入對象(Hibernate從資料庫中取得數據組裝好一個對象後,
* 不會立即再從資料庫取得數據組裝此對象所關聯的對象,而是等到需要時,
* 都會從資料庫取得數據組裝此對象關聯的對象)時,get()方法仍然使用
* 立即載入的方式發送SQL語句,並得到已初始化的對象,而load()方法則
* 根本不發送SQL語句,它返回一個代理對象,直到這個對象被訪問時才被
* 初始化。
*/
get()----不支持LAZY
load()----支持LAZY
轉載:
load和get一共是2個區別 先講第一個 延遲載入
load是true而get是false
意 思就是 load採用的是延遲載入的方式 而get不是,hibernate思想是 既然這個方法支持延遲載入 他就認為這個對象一定在資料庫存在,在你 聲明 TFaq tfag2=(TFaq)sess.load(TFaq.class, 300); 這句時候,hibernate就幹了一件事
1.查詢session緩存
2.緩存中沒有這個對象 就創建個代理
因為延遲載入需要代理來執行 所以就創建了個代理
ok 到此為止 這句話就幹了個這個 並沒有去資料庫交互查詢
當你使用這個對象 比如tfag2.getTfRtitle()或get方法時候
這個時候 hibernate就去查詢二級緩存和資料庫,資料庫沒有這條數據 就拋出異常
整個load方法調用結束 load沒什麼神奇 這就是他干過所有的事情
load方法講完了 我在講一下get方法工作原理
因為hibernate規定get方法不能使用延遲載入 所以和load還是不一樣的
TFaq tfag2=(TFaq)sess.get(TFaq.class, 300);
在創建這條語句時候 我們看看hibernate幹了哪些事
1.get方法首先查詢session緩存 (session緩存就是hibernate的一級緩存 這個概念大家應該清楚吧 )
2.get方法如果在session緩存中找到了該id對應的對象,如果剛好該對象前面是被代理過的,如被load方法使用過,或者被其他關聯對象延遲載入過,那麼返回的還是原先的代理對象,而不是實體類對象。
3.如果該代理對象還沒有載入實體數據(就是id以外的其他屬性數據),那麼它會查詢二級緩存或者資料庫來載入數據,但是返回的還是代理對象,只不過已經載入了實體數據。
(這個代理實際就是空的對象 並沒有去資料庫查詢得到的 我們叫代理對象,如果 去資料庫查詢了 返回到了這個對象 我們叫實體對象 就是這個對象真實存在)
我在總結性一句話這2者區別
get方法首先查詢session緩存,沒有的話查詢二級緩存,最後查詢資料庫;反而load方法創建時首先查詢session緩存,沒有就創建代理,實際使用數據時才查詢二級緩存和資料庫
----我測試過:
在使用session.get方法後如果把session關閉的話,也會出現懶載入異常。
那麼只有在manytoone標簽里配置 lazy="false"時異常才會解決。
也就是說上面轉載的第3條不是那麼正確:返回該代理對象不錯,但是如果該對象沒有載入實體數據,那麼也會在用到時才會載入,即不會立即查詢資料庫或者二級緩存,那麼你現在把session關閉,這個對行啊沒有載入實體數據----才會出現懶載入異常。
⑷ Hibernate的緩存是在何時清除的
除了手動清除外,一級緩存只在同一個session中 有效 ,至於具體何時從內存卸載由Hibernate框架控制,二級緩存是全局性質的,
⑸ 怎樣解決hibernate中一級緩存導致數據不能刷新
Hibernate的一級緩存是由Session提供的,因此它只存在於Session的生命周期中,也就是當Session關閉的時候該Session所管理的一級緩存也會立即被清除。
Hibernate的一級緩存是Session所內置的,不能被卸載,也不能進行任何配置。
一級緩存採用的是key-value的Map方式來實現的,在緩存實體對象時,對象的主關鍵字ID是Map的key,實體對象就是對應的值。所以說,一級緩存是以實體對象為單位進行存儲的,在訪問的時候使用的是主關鍵字ID。
雖然,Hibernate對一級緩存使用的是自動維護的功能,沒有提供任何配置功能,但是可以通過Session中所提供的方法來對一級緩存的管理進行手工干預。Session中所提供的干預方法包括以下兩種。
evict() :用於將某個對象從Session的一級緩存中清除。
clear() :用於將一級緩存中的對象全部清除。
在進行大批量數據一次性更新的時候,會佔用非常多的內存來緩存被更新的對象。這時就應該階段性地調用clear()方法來清空一級緩存中的對象,控制一級緩存的大小,以避免產生內存溢出的情況。
⑹ hibernate中session的緩存怎麼清除
1. 清空緩存
當調用session.evict(customer); 或者session.clear(); 或者session.close()方法時,Session的緩存被清空。
2. 清理緩存
Session具有一個緩存,位於緩存中的對象處於持久化狀態,它和資料庫中的相關記錄對應,Session能夠在某些時間點,按照緩存中持久化對象的屬性變化來同步更新資料庫,這一過程被稱為清理緩存。
在默認情況下,Session會在下面的時間點清理緩存。
當應用程序調用org.hibernate.Transaction的commit()方法的時候,commit()方法先清理緩存,然後在向資料庫提交事務;
當應用程序調用Session的list()或者iterate()時(【注】get()和load()方法不行),如果緩存中持久化對象的屬性發生了變化,就會先清理緩存,以保證查詢結果能能反映持久化對象的最新狀態;
當應用程序顯式調用Session的flush()方法的時候。
⑺ hibernate 一級緩存session關閉後失效。二級緩存什麼時候失效
二級緩存是factory級別的,二級緩存被清除的時候會失效,例如:執行了executeUpdate等語句。
⑻ Java中緩存的問題,session,hibernate的緩存,二級緩存,以及我們把常用的的數據緩存下來,有什麼分別
sessiono由session工廠創建,是一個非常重要的對象,它可以開啟事務(業務中必須用到的),對數據進行增刪改查,創建hql,創建原生sql,創建qbc,等,主要是跟資料庫一級to,po,do對象打交道。
首先設置緩存的目的就是為了減少伺服器壓力提高用戶訪問速度。換才能就好像是我們的內存一樣,而資料庫就好像我們的硬碟一樣,從內存中拿數據肯定要比從硬碟中拿數據快的多。
一級緩存又名session級緩存,就是hibernate查詢資料庫後將查詢結果存放在緩存中,這樣下一次查詢相同數據時就不會從資料庫中拿數據,就可以直接在緩存中拿數據,加快了訪問速度。因為從資料庫中拿數據時費時費力的所以有了緩存就大大減小了伺服器壓力。
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中的二級緩存
第一級別的緩存是Session級別的緩存,是屬於事務范圍的緩存,由Hibernate管理,一般無需進行干預。第二級別的緩存是SessionFactory級別的緩存,是屬於進程范圍的緩存。
二級緩存也分為了兩種
內置緩存:Hibernate自帶的,不可卸載,通常在Hibernate的初始化階段,Hibernate會把映射元數據和預定義的SQL語句放置到SessionFactory的緩存中。該內置緩存是只讀的。
外置緩存:通常說的二級緩存也就是外置緩存,在默認情況下SessionFactory不會啟用這個緩存插件,外置緩存中的數據是資料庫數據的復制,外置緩存的物理介質可以是內存或者硬碟。
hibernate二級緩存的結構
2.並發訪問策略
transactional
(事務型)
僅在受管理的環境中適用
提供Repeatable Read事務隔離級別
適用經常被讀,很少修改的數據
可以防止臟讀和不可重復讀的並發問題
緩存支持事務,發生異常的時候,緩存也能夠回滾
read-write
(讀寫型)
提供Read Committed事務隔離級別
在非集群的環境中適用
適用經常被讀,很少修改的數據
可以防止臟讀
更新緩存的時候會鎖定緩存中的數據
nonstrict-read-write
(非嚴格讀寫型)
適用極少被修改,偶爾允許臟讀的數據(兩個事務同時修改數據的情況很少見)
不保證緩存和資料庫中數據的一致性
為緩存數據設置很短的過期時間,從而盡量避免臟讀
不鎖定緩存中的數據
read-only
(只讀型)
適用從來不會被修改的數據(如參考數據)
在此模式下,如果對數據進行更新操作,會有異常
事務隔離級別低,並發性能高
在集群環境中也能完美運作
分析:通過上述表格分析如下
適合放入二級緩存中數據
很少被修改
不是很重要的數據,允許出現偶爾的並發問題
不適合放入二級緩存中的數據
經常被修改
財務數據,絕對不允許出現並發問題
與其他應用數據共享的數據