MyBatis--查詢緩存

MyBatis--查詢緩存

查詢緩存的使用伐弹,主要是為了提高查詢訪問速度疟游。將用戶對同一數(shù)據(jù)的重復(fù)查詢過程簡化,不再每次均從數(shù)據(jù)庫查詢獲取結(jié)果數(shù)據(jù)鸳兽,從而提高訪問速度。

MyBatis的查詢緩存機制罕拂,根據(jù)緩存區(qū)的作用域(生命周期)可劃分為兩種:一級緩存與二級緩存

一揍异、一級查詢緩存

MyBatis一級緩存是基于org.apache.ibatis.cache.impl.PerpetualCache類的HashMap本地緩存,其作用域是Sqlsession爆班。在同一個Sqlsession中兩次執(zhí)行相同的sql語句衷掷,第一次執(zhí)行完畢后,會將查詢結(jié)果寫入到緩存中柿菩,第二次會從緩存中直接獲取數(shù)據(jù)戚嗅,而不再到數(shù)據(jù)庫中進行查詢,從而提高查詢效率碗旅。

當一個Sqlsession結(jié)束后渡处,該Sqlsession中的一級緩存也就不存在了镜悉。MyBatis默認一級緩存是開啟狀態(tài)祟辟,且不能關(guān)閉。

執(zhí)行原理示意.PNG

1.一級緩存的存在性證明

測試類:

//證明一級緩存的存在
@Test
public void test01(){
    //第一次查詢
    Student student = dao.selectStudentById(2);
    System.out.println(student);
    //第二次查詢
    Student student2 = dao.selectStudentById(2);
    System.out.println(student2);       
}

mapper:

<mapper namespace="com.hcx.dao.IStudentDao">     
    <select id=selectStudentById resultType="com.hcx.beans.Student">
        select * from student where id=#{id}
    </select>
</mapper>

控制臺:

執(zhí)行完后侣肄,發(fā)現(xiàn)只執(zhí)行了一次從DB中的查詢旧困,第二次的結(jié)果是直接輸出的。說明稼锅,第二次是從Sqlsession緩存中讀取的吼具。

2.PNG

2.從緩存讀取數(shù)據(jù)的依據(jù)是sql的id

一級緩存緩存的是相同sql映射id的查詢結(jié)果,而非相同sql語句的查詢結(jié)果矩距。因為MyBatis內(nèi)部對于查詢緩存拗盒,無論是一級查詢還是二級查詢,其底層均使用一個hashmap實現(xiàn):key為sql的id相關(guān)內(nèi)容锥债,value為從數(shù)據(jù)庫中查詢出的結(jié)果陡蝇。

mapper:

<mapper namespace="com.hcx.dao.IStudentDao">
 
    <select id=selectStudentById resultType="com.hcx.beans.Student">
        select * from student where id=#{id}
    </select>
    
    <select id="selectStudnetById2" resultType="com.hcx.beans.Student">
        select id,name,age,score,birthday from student where id=#{id}
    </select>
</mapper>

dao接口:

public interface IStudentDao {      
    Student selectStudentById(int id);
    Student selectStudentById2(int id); 
}

測試類:

//證明從一級緩存中讀取數(shù)據(jù)的依據(jù):
//MyBatis:sql的id+sql語句
//hibernate:查詢結(jié)果對象的id
@Test
public void test02(){
    Student student = dao.selectStudentById(2);
    System.out.println(student);
    
    Student student2 = dao.selectStudentById2(2);
    System.out.println(student2);       
}

控制臺:

查看控制臺,發(fā)現(xiàn)第二次查詢結(jié)果與第一次的完全相同哮肚,但第二次查詢并沒有從緩存中讀取數(shù)據(jù)登夫,而是直接從DB中進行的查詢。這是因為從緩存讀取數(shù)據(jù)的依據(jù)是查詢sql的映射id允趟,而非查詢結(jié)果恼策。

3.PNG

3.增刪改對一級查詢緩存的影響

增刪改操作,無論是否進行提交Sqlsession.commit(),均會清空一級查詢緩存潮剪,使查詢再次從DB中select涣楷。

測試類:

@Test
public void test03(){
    Student student = dao.selectStudentById(2);
    System.out.println(student);
    
    //增刪改操作都會清空一級緩存分唾,無論是否提交
    dao.insertStudent(new Student("趙六",26,96.6));
    
    Student student2 = dao.selectStudentById(2);
    System.out.println(student2);       
}

控制臺:

4.PNG

二、內(nèi)置二級查詢緩存

MyBatis查詢緩存的作用域是根據(jù)映射文件mapper的namespace劃分的狮斗,相同namespace的mapper查詢數(shù)據(jù)存放在同一個緩存區(qū)域鳍寂。不同namespace下的數(shù)據(jù)互不干擾。

無論是一級緩存還是二級緩存情龄,都是按照namespace進行分別存放的迄汛。但一、二級緩存的不同之處在于骤视,Sqlsession一旦關(guān)閉鞍爱,則Sqlsession中的數(shù)據(jù)將不存在,即一級緩存就不復(fù)存在专酗。而二級緩存的生命周期會與整個應(yīng)用同步睹逃,與Sqlsession是否關(guān)閉無關(guān)。

使用二級緩存的目的祷肯,不是共享數(shù)據(jù)沉填,因為MyBatis從緩存中讀取數(shù)據(jù)的依據(jù)是sql的id,而非查詢出的對象佑笋。所以翼闹,二級緩存中的數(shù)據(jù)不是為了在多個查詢之間共享(所有查詢中只要查詢結(jié)果中存在該對象的,就直接從緩存中讀取蒋纬,這是對數(shù)據(jù)的共享猎荠,hibernate中的緩存就是為了共享,但MyBatis不是)蜀备,而是為了延長該查詢結(jié)果的保存時間关摇,提高系統(tǒng)性能。

1.二級緩存用法

二級緩存的使用只需要完成兩步:

  • 序列化實體
  • 在mapper映射文件中添加<cache/>標簽

1.實體序列化

要求查詢結(jié)果所涉及到的實體類要實現(xiàn)java.io.Serializable接口碾阁。若該實體類存在父類输虱,或其具有域?qū)傩裕瑒t父類與域?qū)傩灶愐惨獙崿F(xiàn)序列化接口脂凶。

public class Student implements Serializable{
    
    private Integer id;
    private String name;
    private int age;
    private double score;
}

2.mapper映射文件中添加<cache/>標簽

在mapper映射文件中的<mapper/>標簽中添加<cache/>子標簽

<mapper namespace="com.hcx.dao.IStudentDao">

    <cache/>
 
    <select id=selectStudentById resultType="com.hcx.beans.Student">
        select * from student where id=#{id}
    </select>
</mapper>

3.二級緩存的配置

為<cache/>標簽添加一些相關(guān)屬性設(shè)置宪睹,可以對二級緩存的運行性能進行控制。若不指定設(shè)置艰猬,則均保持默認值横堡。

<cache eviction="IFIO" flushInterval="10800000"
       readOnly="true" size="512"/>
  • eviction:逐出策略。當二級緩存中的對象達到最大值時冠桃,就需要通過逐出策略將緩存中的對象移出緩存命贴。默認為LRU。常用的策略有FIFO和LRU
  • flushInterval:刷新緩存的時間間隔,單位毫秒胸蛛。這里的刷新緩存即清空緩存污茵。一般不指定,即當執(zhí)行增刪改時刷新緩存葬项。
  • readOnly:設(shè)置緩存中數(shù)據(jù)是否只讀泞当。只讀的緩存會給所有調(diào)用者返回緩存對象的相同實例,因此這些對象不能被修改民珍,這提供了很重要的性能優(yōu)勢襟士。但讀寫的緩存會返回緩存對象的拷貝。這會慢一些嚷量,但是安全陋桂,因此默認是false。
  • size:二級緩存中可以存放的最多對象個數(shù)蝶溶。默認為1024個嗜历。

2.二級緩存的存在性證明

對于映射文件中的同一個查詢,肯定是同一個namespace中的查詢抖所。在一次查詢后梨州,將Sqlsession關(guān)閉,再進行一次相同查詢田轧,發(fā)現(xiàn)并沒有到DB中進行select查詢暴匠,說明二級緩存是存在的。

//證明二級緩存的存在
@Test
public void test01(){
    //第一次查詢
    Student student = dao.selectStudentById(2);
    System.out.println(student);
    
    sqlSession.close();
    
    sqlSession = MyBatisUtils.getSqlSession();
    dao = sqlSession.getMapper(IStudentDao.class);
    
    //第二次查詢
    Student student2 = dao.selectStudentById(2);
    System.out.println(student2);       
}

查看控制臺:

Cache Hit Ratio表示緩存命中率涯鲁。開啟二級緩存后巷查,每執(zhí)行一次查詢,系統(tǒng)都會計算一次二級緩存的命中率抹腿。第一次查詢也是先從緩存中查詢,只不過緩存中一定是沒有的旭寿。所以會再從DB中查詢警绩。由于二級緩存中不存在該數(shù)據(jù),所以命中率為0.但第二次查詢是從二級緩存中讀取的盅称,所以這一次的命中率為1/2=0.5肩祥。當然,若有第三次查詢缩膝,則命中率為1/3=0.66

6.PNG

3.增刪改對二級緩存的影響

增刪改操作混狠,無論是否進行提交sqlSession.commit(),均會清空一級疾层、二級緩存将饺,使查詢再次從DB中select。

測試類:

@Test
public void test02(){
    //第一次查詢
    Student student = dao.selectStudentById(2);
    System.out.println(student);
    
    sqlSession.close();
    
    sqlSession = MyBatisUtils.getSqlSession();
    dao = sqlSession.getMapper(IStudentDao.class);
    
    //插入
    dao.insertStudent(new Student("",0,0));
    
    //第二次查詢
    Student student2 = dao.selectStudentById(2);
    System.out.println(student2);       
}

控制臺:

注意,在第二次查詢時的緩存命中率為0.5予弧,但還是從DB中查詢了刮吧。說明在緩存中與該查詢相對應(yīng)的key是存在的,但其value被清空掖蛤。而value被清空的原因是前面執(zhí)行了對DB的增刪改操作杀捻,所以不會從緩存中直接將null值返回,而是從DB中進行查詢蚓庭。

9.PNG

說明:

  • 二級緩存的清空致讥,實質(zhì)上是對所查找key對應(yīng)的value置為null,而非將<key,value>對器赞,即entry對象刪除拄踪。
  • 從DB中進行select查詢的條件是:緩存中根本不存在這個key或者緩存中存在該key所對應(yīng)的entry對象,但value為null拳魁。

設(shè)置增刪改操作不刷新二級緩存:

若要使某個增惶桐、刪或改操作不清空二級緩存,則需要在其<insert/>或<delete/>或<update/>中添加屬性flushCache="false"潘懊,默認為true姚糊。

<insert id="insertStudent"  flushCache="false">
    insert into student(name,age,score) values(#{name},#{age},#{score})
</insert>

4.二級緩存的關(guān)閉

二級緩存默認為開啟狀態(tài)。若要將其關(guān)閉授舟,則需要進行相關(guān)設(shè)置救恨。
根據(jù)關(guān)閉的范圍大小,可以分為全局關(guān)閉和局部關(guān)閉

1.全局關(guān)閉(在配置文件中設(shè)置)

全局關(guān)閉是指整個應(yīng)用的二級緩存全部關(guān)閉释树,所有查詢均不使用二級緩存肠槽。全局開關(guān)設(shè)置在主配置文件的全局設(shè)置<settings/>中,該屬性為cacheEnabled奢啥,設(shè)置為false秸仙,則關(guān)閉;設(shè)置為true桩盲,則開啟寂纪,默認值為true。即二級緩存默認是開啟的赌结。

<!-- 關(guān)閉二級緩存 -->
<settings>
    <setting name="cacheEnabled" value="false"/>
</settings>

2.局部關(guān)閉(在映射文件的每個select中設(shè)置)

局部關(guān)閉指整個應(yīng)用的二級緩存是開啟的捞蛋,但只是針對某個<select/>查詢,不使用二級緩存柬姚。此時可以單獨只關(guān)閉該<select/>標簽的二級緩存拟杉。

在該要關(guān)閉二級緩存的<select/>標簽中,將其屬性useCache設(shè)置為false量承,即可關(guān)閉該查詢的二級緩存搬设。該屬性默認為true穴店,即每個<select/>查詢的二級緩存默認是開啟的。

<!--useCache="false"對當前sql的二級緩存的局部關(guān)閉  -->
<select id=selectStudentById useCache="false" resultType="com.hcx.beans.Student">
    select * from student where id=#{id}
</select>

5.二級緩存的使用原則

1.只能在一個命名空間下使用二級緩存

由于二級緩存中的數(shù)據(jù)是基于namespace的焕梅,即不同namespace中的數(shù)據(jù)互不干擾迹鹅。在多個namespace中若均存在對同一個表的操作,那么這多個namespace中的數(shù)據(jù)可能就會出現(xiàn)不一致現(xiàn)象贞言。

2.在單表上使用二級緩存

如果一個表與其它表有關(guān)聯(lián)關(guān)系斜棚,那么久非常有可能存在多個namespace對同一數(shù)據(jù)的操作。而不同namespace中的數(shù)據(jù)互補干擾该窗,所以就有可能出現(xiàn)多個namespace中的數(shù)據(jù)不一致現(xiàn)象弟蚀。

3.查詢多于修改時使用二級緩存

在查詢操作遠遠多于增刪改操作的情況下可以使用二級緩存。因為任何增刪改操作都將刷新二級緩存酗失,對二級緩存的頻繁刷新將降低系統(tǒng)性能义钉。

三、ehcache二級查詢緩存

MyBatis允許使用第三方緩存產(chǎn)品规肴。ehCache就是其中一種捶闸。

注意:使用ehcache二級緩存,實體類無需實現(xiàn)序列化接口拖刃。

1.導(dǎo)入jar包

jar.PNG

2.添加ehcache.xml

解壓ehcache的核心jar包ehcache-core-2.6.8.jar删壮,將其中的一個配置文件ehcache-failsafe.xml直接放到項目的src目錄下,并更名為ehcache.xml

(1)<diskStore/>標簽

指定一個文件目錄兑牡,當內(nèi)存空間不夠央碟,需要將二級緩存中數(shù)據(jù)寫到硬盤上時,會寫到這個指定目錄中均函。其值一般為java.io.tmpdir亿虽,表示當前系統(tǒng)的默認文件臨時目錄。

diskstore.PNG

當前文件系統(tǒng)的默認文件臨時目錄苞也,可以通過System.property()方法查看:

@Test
public void test(){
    String path = System.getProperty("java.io.tmpdir");
    System.out.println(path);
}

(2)<defaultCache/>標簽

defaultcache.PNG
default1.PNG

3.啟用ehcache緩存機制

在映射文件的mapper中的<cache/>中通過type指定緩存機制為Ehcache緩存洛勉。默認為MyBatis內(nèi)置的二級緩存org.apache.ibatis.cache.impl.PerpetualCache。

<mapper namespace="com.hcx.dao.IStudentDao">

    <cache type="org.mybatis.caches.ehcache.EhcacheCache"/>
        
    <select id=selectStudentById resultType="com.hcx.beans.Student">
        select * from student where id=#{id}
    </select>
</mapper>

4.ehcache在不同mapper中的個性化設(shè)置

在ehcache.xml中設(shè)置的屬性值墩朦,會對該項目中所有使用ehcache緩存機制的緩存區(qū)域起作用坯认。一個項目中可以有多個mapper,不同的mapper有不同的緩存區(qū)域氓涣。對于不同緩存區(qū)域也可進行專門針對于當前區(qū)域的個性設(shè)置,可通過指定不同mapper的<cache>屬性值來設(shè)置陋气。

<cache>屬性值的優(yōu)先級高于ehcache.xml中的屬性值劳吠。

<mapper namespace="com.hcx.dao.IStudentDao">

    <cache type="org.mybatis.caches.ehcache.EhcacheCache"/>
        <property name="maxElementsInMemory" value="5000"/>
        <property name="timeToIdleSeconds" value="240"/>
    </cache>
</mapper>
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市巩趁,隨后出現(xiàn)的幾起案子痒玩,更是在濱河造成了極大的恐慌淳附,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蠢古,死亡現(xiàn)場離奇詭異奴曙,居然都是意外死亡,警方通過查閱死者的電腦和手機草讶,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進店門洽糟,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人堕战,你說我怎么就攤上這事坤溃。” “怎么了嘱丢?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵薪介,是天一觀的道長。 經(jīng)常有香客問我越驻,道長汁政,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任缀旁,我火速辦了婚禮记劈,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘诵棵。我一直安慰自己抠蚣,他們只是感情好,可當我...
    茶點故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布履澳。 她就那樣靜靜地躺著嘶窄,像睡著了一般。 火紅的嫁衣襯著肌膚如雪距贷。 梳的紋絲不亂的頭發(fā)上柄冲,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天,我揣著相機與錄音忠蝗,去河邊找鬼现横。 笑死,一個胖子當著我的面吹牛阁最,可吹牛的內(nèi)容都是我干的戒祠。 我是一名探鬼主播,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼速种,長吁一口氣:“原來是場噩夢啊……” “哼姜盈!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起配阵,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤馏颂,失蹤者是張志新(化名)和其女友劉穎示血,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體救拉,經(jīng)...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡难审,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了亿絮。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片告喊。...
    茶點故事閱讀 38,018評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖壹无,靈堂內(nèi)的尸體忽然破棺而出葱绒,到底是詐尸還是另有隱情,我是刑警寧澤斗锭,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布地淀,位于F島的核電站,受9級特大地震影響岖是,放射性物質(zhì)發(fā)生泄漏帮毁。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一豺撑、第九天 我趴在偏房一處隱蔽的房頂上張望烈疚。 院中可真熱鬧,春花似錦聪轿、人聲如沸爷肝。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽灯抛。三九已至,卻和暖如春音瓷,著一層夾襖步出監(jiān)牢的瞬間对嚼,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工绳慎, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留纵竖,地道東北人。 一個月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓杏愤,卻偏偏與公主長得像靡砌,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子珊楼,可洞房花燭夜當晚...
    茶點故事閱讀 42,762評論 2 345

推薦閱讀更多精彩內(nèi)容