MyBatis】MyBatis一级缓存和二级缓存
轉(zhuǎn)載自??MyBatis】MyBatis一級緩存和二級緩存
MyBatis自帶的緩存有一級緩存和二級緩存
一級緩存
Mybatis的一級緩存是指Session緩存。一級緩存的作用域默認(rèn)是一個SqlSession。Mybatis默認(rèn)開啟一級緩存。?
也就是在同一個SqlSession中,執(zhí)行相同的查詢SQL,第一次會去數(shù)據(jù)庫進(jìn)行查詢,并寫到緩存中;?
第二次以后是直接去緩存中取。?
當(dāng)執(zhí)行SQL查詢中間發(fā)生了增刪改的操作,MyBatis會把SqlSession的緩存清空。
一級緩存的范圍有SESSION和STATEMENT兩種,默認(rèn)是SESSION,如果不想使用一級緩存,可以把一級緩存的范圍指定為STATEMENT,這樣每次執(zhí)行完一個Mapper中的語句后都會將一級緩存清除。?
如果需要更改一級緩存的范圍,可以在Mybatis的配置文件中,在下通過localCacheScope指定。
建議不需要修改
需要注意的是?
當(dāng)Mybatis整合Spring后,直接通過Spring注入Mapper的形式,如果不是在同一個事務(wù)中每個Mapper的每次查詢操作都對應(yīng)一個全新的SqlSession實(shí)例,這個時候就不會有一級緩存的命中,但是在同一個事務(wù)中時共用的是同一個SqlSession。?
如有需要可以啟用二級緩存。
二級緩存
Mybatis的二級緩存是指mapper映射文件。二級緩存的作用域是同一個namespace下的mapper映射文件內(nèi)容,多個SqlSession共享。Mybatis需要手動設(shè)置啟動二級緩存。
二級緩存是默認(rèn)啟用的(要生效需要對每個Mapper進(jìn)行配置),如想取消,則可以通過Mybatis配置文件中的元素下的子元素來指定cacheEnabled為false。
<settings><setting name="cacheEnabled" value="false" /> </settings>cacheEnabled默認(rèn)是啟用的,只有在該值為true的時候,底層使用的Executor才是支持二級緩存的CachingExecutor。具體可參考Mybatis的核心配置類org.apache.ibatis.session.Configuration的newExecutor方法實(shí)現(xiàn)。?
可以通過源碼看看
要使用二級緩存除了上面一個配置外,我們還需要在我們每個DAO對應(yīng)的Mapper.xml文件中定義需要使用的cache
... <mapper namespace="...UserMapper"><cache/><!-- 加上該句即可,使用默認(rèn)配置、還有另外一種方式,在后面寫出 -->... </mapper>具體可以看org.apache.ibatis.executor.CachingExecutor類的以下實(shí)現(xiàn)?
其中使用的cache就是我們在對應(yīng)的Mapper.xml中定義的cache。
還有一個條件就是需要當(dāng)前的查詢語句是配置了使用cache的,即上面源碼的useCache()是返回true的,默認(rèn)情況下所有select語句的useCache都是true,如果我們在啟用了二級緩存后,有某個查詢語句是我們不想緩存的,則可以通過指定其useCache為false來達(dá)到對應(yīng)的效果。?
如果我們不想該語句緩存,可使用useCache=”false
cache定義的兩種使用方式
上面說了要想使用二級緩存,需要在每個DAO對應(yīng)的Mapper.xml文件中定義其中的查詢語句需要使用cache來緩存數(shù)據(jù)的。?
這有兩種方式可以定義,一種是通過cache元素定義,一種是通過cache-ref元素來定義。?
需要注意的是?
對于同一個Mapper來講,只能使用一個Cache,當(dāng)同時使用了和時,定義的優(yōu)先級更高(后面的代碼會給出原因)。?
Mapper使用的Cache是與我們的Mapper對應(yīng)的namespace綁定的,一個namespace最多只會有一個Cache與其綁定。
cache元素定義
使用cache元素來定義使用的Cache時,最簡單的做法是直接在對應(yīng)的Mapper.xml文件中指定一個空的元素(看前面的代碼),這個時候Mybatis會按照默認(rèn)配置創(chuàng)建一個Cache對象,準(zhǔn)備的說是PerpetualCache對象,更準(zhǔn)確的說是LruCache對象(底層用了裝飾器模式)。?
具體的可看org.apache.ibatis.builder.xml.XMLMapperBuilder中的cacheElement()方法解析cache元素的邏輯。
空cache元素定義會生成一個采用最近最少使用算法最多只能存儲1024個元素的緩存,而且是可讀寫的緩存,即該緩存是全局共享的,任何一個線程在拿到緩存結(jié)果后對數(shù)據(jù)的修改都將影響其它線程獲取的緩存結(jié)果,因?yàn)樗鼈兪枪蚕淼?#xff0c;同一個對象。
cache元素可指定如下屬性,每種屬性的指定都是針對都是針對底層Cache的一種裝飾,采用的是裝飾器的模式。
blocking:默認(rèn)為false,當(dāng)指定為true時將采用BlockingCache進(jìn)行封裝,blocking,阻塞的意思,使用BlockingCache會在查詢緩存時鎖住對應(yīng)的Key,如果緩存命中了則會釋放對應(yīng)的鎖,否則會在查詢數(shù)據(jù)庫以后再釋放鎖,這樣可以阻止并發(fā)情況下多個線程同時查詢數(shù)據(jù),詳情可參考BlockingCache的源碼。?
簡單理解,也就是設(shè)置true時,在進(jìn)行增刪改之后的并發(fā)查詢,只會有一條去數(shù)據(jù)庫查詢,而不會并發(fā)
eviction:eviction,驅(qū)逐的意思。也就是元素驅(qū)逐算法,默認(rèn)是LRU,對應(yīng)的就是LruCache,其默認(rèn)只保存1024個Key,超出時按照最近最少使用算法進(jìn)行驅(qū)逐,詳情請參考LruCache的源碼。如果想使用自己的算法,則可以將該值指定為自己的驅(qū)逐算法實(shí)現(xiàn)類,只需要自己的類實(shí)現(xiàn)Mybatis的Cache接口即可。除了LRU以外,系統(tǒng)還提供了FIFO(先進(jìn)先出,對應(yīng)FifoCache)、SOFT(采用軟引用存儲Value,便于垃圾回收,對應(yīng)SoftCache)和WEAK(采用弱引用存儲Value,便于垃圾回收,對應(yīng)WeakCache)這三種策略。?
這里,根據(jù)個人需求選擇了,沒什么要求的話,默認(rèn)的LRU即可
flushInterval:清空緩存的時間間隔,單位是毫秒,默認(rèn)是不會清空的。當(dāng)指定了該值時會再用ScheduleCache包裝一次,其會在每次對緩存進(jìn)行操作時判斷距離最近一次清空緩存的時間是否超過了flushInterval指定的時間,如果超出了,則清空當(dāng)前的緩存,詳情可參考ScheduleCache的實(shí)現(xiàn)。
readOnly:是否只讀?
默認(rèn)為false。當(dāng)指定為false時,底層會用SerializedCache包裝一次,其會在寫緩存的時候?qū)⒕彺鎸ο筮M(jìn)行序列化,然后在讀緩存的時候進(jìn)行反序列化,這樣每次讀到的都將是一個新的對象,即使你更改了讀取到的結(jié)果,也不會影響原來緩存的對象,即非只讀,你每次拿到這個緩存結(jié)果都可以進(jìn)行修改,而不會影響原來的緩存結(jié)果;?
當(dāng)指定為true時那就是每次獲取的都是同一個引用,對其修改會影響后續(xù)的緩存數(shù)據(jù)獲取,這種情況下是不建議對獲取到的緩存結(jié)果進(jìn)行更改,意為只讀(不建議設(shè)置為true)。?
這是Mybatis二級緩存讀寫和只讀的定義,可能與我們通常情況下的只讀和讀寫意義有點(diǎn)不同。每次都進(jìn)行序列化和反序列化無疑會影響性能,但是這樣的緩存結(jié)果更安全,不會被隨意更改,具體可根據(jù)實(shí)際情況進(jìn)行選擇。詳情可參考SerializedCache的源碼。
size:用來指定緩存中最多保存的Key的數(shù)量。其是針對LruCache而言的,LruCache默認(rèn)只存儲最多1024個Key,可通過該屬性來改變默認(rèn)值,當(dāng)然,如果你通過eviction指定了自己的驅(qū)逐算法,同時自己的實(shí)現(xiàn)里面也有setSize方法,那么也可以通過cache的size屬性給自定義的驅(qū)逐算法里面的size賦值。
type:type屬性用來指定當(dāng)前底層緩存實(shí)現(xiàn)類,默認(rèn)是PerpetualCache,如果我們想使用自定義的Cache,則可以通過該屬性來指定,對應(yīng)的值是我們自定義的Cache的全路徑名稱。
cache-ref元素定義
cache-ref元素可以用來指定其它Mapper.xml中定義的Cache,有的時候可能我們多個不同的Mapper需要共享同一個緩存的?
是希望在MapperA中緩存的內(nèi)容在MapperB中可以直接命中的,這個時候我們就可以考慮使用cache-ref,這種場景只需要保證它們的緩存的Key是一致的即可命中,二級緩存的Key是通過Executor接口的createCacheKey()方法生成的,其實(shí)現(xiàn)基本都是BaseExecutor,源碼如下。
打個比方我想在MenuMapper.xml中的查詢都使用在UserMapper.xml中定義的Cache,則可以通過cache-ref元素的namespace屬性指定需要引用的Cache所在的namespace,即UserMapper.xml中的定義的namespace,假設(shè)在UserMapper.xml中定義的namespace是cn.chenhaoxiang.dao.UserMapper,則在MenuMapper.xml的cache-ref應(yīng)該定義如下。
<cache-ref namespace="cn.chenhaoxiang.dao.UserMapper"/>-
1
這樣這兩個Mapper就共享同一個緩存了
自定義cache就不介紹了。
測試二級緩存
?
查詢測試
/*** Created with IntelliJ IDEA.* User: 陳浩翔.* Date: 2018/1/10.* Time: 下午 10:15.* Explain:*/@RunWith(SpringJUnit4ClassRunner.class)//配置了@ContextConfiguration注解并使用該注解的locations屬性指明spring和配置文件之后@ContextConfiguration(locations = {"classpath:spring.xml","classpath:spring-mybatis.xml"})public class MyBatisTestBySpringTestFramework {//注入userService@Autowiredprivate UserService userService; ? ?@Testpublic void testGetUserId(){String userId = "4e07f3963337488e81716cfdd8a0fe04";User user = userService.getUserById(userId);System.out.println(user); ? ? ? ?//前面說到spring和MyBatis整合User user2 = userService.getUserById(userId);System.out.println("user2:"+user2);} }?
接下來我們把Mapper中的cache元素刪除,不使用二級緩存
?
再運(yùn)行測試
?
對二級緩存進(jìn)行了以下測試,獲取兩個不同的SqlSession(前面有說,Spring和MyBatis集成,每次都是不同的SqlSession)執(zhí)行兩條相同的SQL,在未指定Cache時Mybatis將查詢兩次數(shù)據(jù)庫,在指定了Cache時Mybatis只查詢了一次數(shù)據(jù)庫,第二次是從緩存中拿的。
Cache Hit Ratio 表示緩存命中率。?
開啟二級緩存后,每執(zhí)行一次查詢,系統(tǒng)都會計(jì)算一次二級緩存的命中率。?
第一次查詢也是先從緩存中查詢,只不過緩存中一定是沒有的。?
所以會再從DB中查詢。由于二級緩存中不存在該數(shù)據(jù),所以命中率為0.但第二次查詢是從二級緩存中讀取的,所以這一次的命中率為1/2=0.5。?
當(dāng)然,若有第三次查詢,則命中率為1/3=0.66?
0.5這個值可以從上面開啟cache的圖看出來,0.0的值未截取到~漏掉了~
注意:?
增刪改操作,無論是否進(jìn)行提交sqlSession.commit(),均會清空一級、二級緩存,使查詢再次從DB中select。?
說明:?
二級緩存的清空,實(shí)質(zhì)上是對所查找key對應(yīng)的value置為null,而非將
二級緩存的使用原則
只能在一個命名空間下使用二級緩存?
由于二級緩存中的數(shù)據(jù)是基于namespace的,即不同namespace中的數(shù)據(jù)互不干擾。在多個namespace中若均存在對同一個表的操作,那么這多個namespace中的數(shù)據(jù)可能就會出現(xiàn)不一致現(xiàn)象。
在單表上使用二級緩存?
如果一個表與其它表有關(guān)聯(lián)關(guān)系,那么久非常有可能存在多個namespace對同一數(shù)據(jù)的操作。而不同namespace中的數(shù)據(jù)互補(bǔ)干擾,所以就有可能出現(xiàn)多個namespace中的數(shù)據(jù)不一致現(xiàn)象。
查詢多于修改時使用二級緩存?
在查詢操作遠(yuǎn)遠(yuǎn)多于增刪改操作的情況下可以使用二級緩存。因?yàn)槿魏卧鰟h改操作都將刷新二級緩存,對二級緩存的頻繁刷新將降低系統(tǒng)性能。
總結(jié)
以上是生活随笔為你收集整理的MyBatis】MyBatis一级缓存和二级缓存的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 白色电脑好看还是黑色电脑好看电脑白色和黑
- 下一篇: 两个月拿到N个offer,看看我是如何做