mybatis的缓存机制(一级缓存二级缓存和刷新缓存)和mybatis整合ehcache
1??????查詢緩存
?
1.1??什么是查詢緩存
mybatis提供查詢緩存,用于減輕數(shù)據(jù)壓力,提高數(shù)據(jù)庫性能。
mybaits提供一級緩存,和二級緩存。
?
一級緩存是SqlSession級別的緩存。在操作數(shù)據(jù)庫時需要構(gòu)造 sqlSession對象,在對象中有一個(內(nèi)存區(qū)域)數(shù)據(jù)結(jié)構(gòu)(HashMap)用于存儲緩存數(shù)據(jù)。不同的sqlSession之間的緩存數(shù)據(jù)區(qū)域(HashMap)是互相不影響的。
一級緩存的作用域是同一個SqlSession,在同一個sqlSession中兩次執(zhí)行相同的sql語句,第一次執(zhí)行完畢會將數(shù)據(jù)庫中查詢的數(shù)據(jù)寫到緩存(內(nèi)存),第二次會從緩存中獲取數(shù)據(jù)將不再從數(shù)據(jù)庫查詢,從而提高查詢效率。當(dāng)一個sqlSession結(jié)束后該sqlSession中的一級緩存也就不存在了。Mybatis默認(rèn)開啟一級緩存。
二級緩存是mapper級別的緩存,多個SqlSession去操作同一個Mapper的sql語句,多個SqlSession去操作數(shù)據(jù)庫得到數(shù)據(jù)會存在二級緩存區(qū)域,多個SqlSession可以共用二級緩存,二級緩存是跨SqlSession的。
????二級緩存是多個SqlSession共享的,其作用域是mapper的同一個namespace,不同的sqlSession兩次執(zhí)行相同namespace下的sql語句且向sql中傳遞參數(shù)也相同即最終執(zhí)行相同的sql語句,第一次執(zhí)行完畢會將數(shù)據(jù)庫中查詢的數(shù)據(jù)寫到緩存(內(nèi)存),第二次會從緩存中獲取數(shù)據(jù)將不再從數(shù)據(jù)庫查詢,從而提高查詢效率。Mybatis默認(rèn)沒有開啟二級緩存需要在setting全局參數(shù)中配置開啟二級緩存。
如果緩存中有數(shù)據(jù)就不用從數(shù)據(jù)庫中獲取,大大提高系統(tǒng)性能。
1.2??一級緩存
1.2.1????一級緩存工作原理
下圖是根據(jù)id查詢用戶的一級緩存圖解
?
第一次發(fā)起查詢用戶id為1的用戶信息,先去找緩存中是否有id為1的用戶信息,如果沒有,從數(shù)據(jù)庫查詢用戶信息。
得到用戶信息,將用戶信息存儲到一級緩存中。
?
如果sqlSession去執(zhí)行commit操作(執(zhí)行插入、更新、刪除),清空SqlSession中的一級緩存,這樣做的目的為了讓緩存中存儲的是最新的信息,避免臟讀。
?
第二次發(fā)起查詢用戶id為1的用戶信息,先去找緩存中是否有id為1的用戶信息,緩存中有,直接從緩存中獲取用戶信息。
1.2.2????一級緩存測試
mybatis默認(rèn)支持一級緩存,不需要在配置文件去配置。
?
按照上邊一級緩存原理步驟去測試。
?
@Test
???public void testCache1() throws Exception{
??????SqlSessionsqlSession = sqlSessionFactory.openSession();//創(chuàng)建代理對象
??????UserMapperuserMapper = sqlSession.getMapper(UserMapper.class);
?????
??????//下邊查詢使用一個SqlSession
??????//第一次發(fā)起請求,查詢id為1的用戶
??????Useruser1 = userMapper.findUserById(1);
??????System.out.println(user1);
?????
//????如果sqlSession去執(zhí)行commit操作(執(zhí)行插入、更新、刪除),清空SqlSession中的一級緩存,這樣做的目的為了讓緩存中存儲的是最新的信息,避免臟讀。
?????
??????//更新user1的信息
??????user1.setUsername("測試用戶22");
??????userMapper.updateUser(user1);
??????//執(zhí)行commit操作去清空緩存
??????sqlSession.commit();
?????
??????//第二次發(fā)起請求,查詢id為1的用戶
??????Useruser2 = userMapper.findUserById(1);
??????System.out.println(user2);
?????
??????sqlSession.close();
?????
???}
1.2.3????一級緩存應(yīng)用
正式開發(fā),是將mybatis和spring進(jìn)行整合開發(fā),事務(wù)控制在service中。
一個service方法中包括很多mapper方法調(diào)用。
service{
?????????//開始執(zhí)行時,開啟事務(wù),創(chuàng)建SqlSession對象
?????????//第一次調(diào)用mapper的方法findUserById(1)
????????
?????????//第二次調(diào)用mapper的方法findUserById(1),從一級緩存中取數(shù)據(jù)
????????
//aop控制 只要方法結(jié)束,sqlSession關(guān)閉 sqlsession關(guān)閉后就銷毀數(shù)據(jù)結(jié)構(gòu),清空緩存
?????????Service結(jié)束sqlsession關(guān)閉
}
如果是執(zhí)行兩次service調(diào)用查詢相同的用戶信息,不走一級緩存,因為Service方法結(jié)束,sqlSession就關(guān)閉,一級緩存就清空。
1.3??二級緩存
1.3.1????原理
?
首先開啟mybatis的二級緩存。
?
sqlSession1去查詢用戶id為1的用戶信息,查詢到用戶信息會將查詢數(shù)據(jù)存儲到二級緩存中。
?
如果SqlSession3去執(zhí)行相同 mapper下sql,執(zhí)行commit提交,清空該 mapper下的二級緩存區(qū)域的數(shù)據(jù)。
?
sqlSession2去查詢用戶id為1的用戶信息,去緩存中找是否存在數(shù)據(jù),如果存在直接從緩存中取出數(shù)據(jù)。
?
二級緩存與一級緩存區(qū)別,二級緩存的范圍更大,多個sqlSession可以共享一個UserMapper的二級緩存區(qū)域。數(shù)據(jù)類型仍然為HashMap
UserMapper有一個二級緩存區(qū)域(按namespace分,如果namespace相同則使用同一個相同的二級緩存區(qū)),其它mapper也有自己的二級緩存區(qū)域(按namespace分)。
每一個namespace的mapper都有一個二緩存區(qū)域,兩個mapper的namespace如果相同,這兩個mapper執(zhí)行sql查詢到數(shù)據(jù)將存在相同的二級緩存區(qū)域中。
1.3.2????開啟二級緩存
mybaits的二級緩存是mapper范圍級別,除了在SqlMapConfig.xml設(shè)置二級緩存的總開關(guān),還要在具體的mapper.xml中開啟二級緩存。
?
在核心配置文件SqlMapConfig.xml中加入
<setting name="cacheEnabled"value="true"/>
<!-- 全局配置參數(shù),需要時再設(shè)置 -->
????<settings>
???????<!-- 開啟二級緩存??默認(rèn)值為true -->
????<setting name="cacheEnabled" value="true"/>
????</settings>
?
描述
允許值
默認(rèn)值
cacheEnabled
對在此配置文件下的所有cache 進(jìn)行全局性開/關(guān)設(shè)置。
true false
true
?
在UserMapper.xml中開啟二緩存,UserMapper.xml下的sql執(zhí)行完成會存儲到它的緩存區(qū)域(HashMap)。
<mapper namespace="cn.hpu.mybatis.mapper.UserMapper">
<!-- 開啟本mapper namespace下的二級緩存 -->
<cache></cache>
1.3.3????調(diào)用pojo類實現(xiàn)序列化接口
public class Userimplements Serializable {
????//Serializable實現(xiàn)序列化,為了將來反序列化
?
二級緩存需要查詢結(jié)果映射的pojo對象實現(xiàn)java.io.Serializable接口實現(xiàn)序列化和反序列化操作,注意如果存在父類、成員pojo都需要實現(xiàn)序列化接口。
pojo類實現(xiàn)序列化接口是為了將緩存數(shù)據(jù)取出執(zhí)行反序列化操作,因為二級緩存數(shù)據(jù)存儲介質(zhì)多種多樣,不一定在內(nèi)存有可能是硬盤或者遠(yuǎn)程服務(wù)器。
1.3.4????測試方法
?
// 二級緩存測試
???@Test
???public void testCache2() throws Exception {
??????SqlSessionsqlSession1 = sqlSessionFactory.openSession();
??????SqlSessionsqlSession2 = sqlSessionFactory.openSession();
??????SqlSessionsqlSession3 = sqlSessionFactory.openSession();
??????// 創(chuàng)建代理對象
??????UserMapperuserMapper1 = sqlSession1.getMapper(UserMapper.class);
??????// 第一次發(fā)起請求,查詢id為1的用戶
??????Useruser1 = userMapper1.findUserById(1);
??????System.out.println(user1);
?????
??????//這里執(zhí)行關(guān)閉操作,將sqlsession中的數(shù)據(jù)寫到二級緩存區(qū)域
??????sqlSession1.close();
?????
??????//使用sqlSession3執(zhí)行commit()操作
??????UserMapperuserMapper3 = sqlSession3.getMapper(UserMapper.class);
??????Useruser??= userMapper3.findUserById(1);
??????user.setUsername("張明明");
??????userMapper3.updateUser(user);
??????//執(zhí)行提交,清空UserMapper下邊的二級緩存
??????sqlSession3.commit();
??????sqlSession3.close();
?????
??????UserMapperuserMapper2 = sqlSession2.getMapper(UserMapper.class);
??????// 第二次發(fā)起請求,查詢id為1的用戶
??????Useruser2 = userMapper2.findUserById(1);
??????System.out.println(user2);
?
??????sqlSession2.close();
???}
1.3.5??useCache配置禁用二級緩存
在statement中設(shè)置useCache=false可以禁用當(dāng)前select語句的二級緩存,即每次查詢都會發(fā)出sql去查詢,默認(rèn)情況是true,即該sql使用二級緩存。
<selectid="findOrderListResultMap" resultMap="ordersUserMap" useCache="false">
總結(jié):針對每次查詢都需要最新的數(shù)據(jù)sql,要設(shè)置成useCache=false,禁用二級緩存。?
1.3.6????mybatis刷新緩存(就是清空緩存)
在mapper的同一個namespace中,如果有其它insert、update、delete操作數(shù)據(jù)后需要刷新緩存,如果不執(zhí)行刷新緩存會出現(xiàn)臟讀。
?設(shè)置statement配置中的flushCache="true"?屬性,默認(rèn)情況下為true即刷新緩存,如果改成false則不會刷新。使用緩存時如果手動修改數(shù)據(jù)庫表中的查詢數(shù)據(jù)會出現(xiàn)臟讀。
如下:
<insertid="insertUser" parameterType="cn.itcast.mybatis.po.User" flushCache="true">
?
總結(jié):一般下執(zhí)行完commit操作都需要刷新緩存,flushCache=true表示刷新緩存默認(rèn)情況下為true,我們不用去設(shè)置它,這樣可以避免數(shù)據(jù)庫臟讀。
1.3.7??Mybatis Cache參數(shù)
flushInterval(刷新間隔)可以被設(shè)置為任意的正整數(shù),而且它們代表一個合理的毫秒形式的時間段。默認(rèn)情況是不設(shè)置,也就是沒有刷新間隔,緩存僅僅調(diào)用語句時刷新。
size(引用數(shù)目)可以被設(shè)置為任意正整數(shù),要記住你緩存的對象數(shù)目和你運行環(huán)境的可用內(nèi)存資源數(shù)目。默認(rèn)值是1024。
readOnly(只讀)屬性可以被設(shè)置為true或false。只讀的緩存會給所有調(diào)用者返回緩存對象的相同實例。因此這些對象不能被修改。這提供了很重要的性能優(yōu)勢。可讀寫的緩存會返回緩存對象的拷貝(通過序列化)。這會慢一些,但是安全,因此默認(rèn)是false。
如下例子:
<cache ?eviction="FIFO"?flushInterval="60000"??size="512"?readOnly="true"/>
這個更高級的配置創(chuàng)建了一個?FIFO?緩存,并每隔?60?秒刷新,存數(shù)結(jié)果對象或列表的?512?個引用,而且返回的對象被認(rèn)為是只讀的,因此在不同線程中的調(diào)用者之間修改它們會導(dǎo)致沖突。可用的收回策略有,?默認(rèn)的是?LRU:
1.??????LRU?–?最近最少使用的:移除最長時間不被使用的對象。
2.??????FIFO?–?先進(jìn)先出:按對象進(jìn)入緩存的順序來移除它們。
3.??????SOFT?–?軟引用:移除基于垃圾回收器狀態(tài)和軟引用規(guī)則的對象。
4.??????WEAK?–?弱引用:更積極地移除基于垃圾收集器狀態(tài)和弱引用規(guī)則的對象。
1.4??mybatis整合ehcache
ehcache是一個分布式緩存框架。
EhCache 是一個純Java的進(jìn)程內(nèi)緩存框架,是一種廣泛使用的開源Java分布式緩存,具有快速、精干等特點,是Hibernate中默認(rèn)的CacheProvider。
1.4.1????分布緩存
我們系統(tǒng)為了提高系統(tǒng)并發(fā),性能、一般對系統(tǒng)進(jìn)行分布式部署(集群部署方式)
不使用分布緩存,緩存的數(shù)據(jù)在各各服務(wù)單獨存儲,不方便系統(tǒng)開發(fā)。所以要使用分布式緩存對緩存數(shù)據(jù)進(jìn)行集中管理。
mybatis無法實現(xiàn)分布式緩存,需要和其它分布式緩存框架進(jìn)行整合。
1.4.2????整合方法(掌握無論整合誰,首先想到改type接口)
mybatis提供了一個cache接口,如果要實現(xiàn)自己的緩存邏輯,實現(xiàn)cache接口開發(fā)即可。
mybatis和ehcache整合,mybatis和ehcache整合包中提供了一個cache接口的實現(xiàn)類。
1.4.3????第一步加入ehcache包
?
1.4.4????整合ehcache
配置mapper中cache中的type為ehcache對cache接口的實現(xiàn)類型。
?
<mapper namespace="cn.hpu.mybatis.mapper.UserMapper">
<!-- 開啟本mapper namespace下的二級緩存
???type:指定cache接口實現(xiàn)類,mybatis默認(rèn)使用PerpetualCache
???要和eache整合,需要配置type為ehcahe實現(xiàn)cache接口的類型
?-->
<cache type="org.mybatis.caches.ehcache.EhcacheCache">
</cache>
?
可以根據(jù)需求調(diào)整緩存參數(shù):
<cache type="org.mybatis.caches.ehcache.EhcacheCache">
????????<property name="timeToIdleSeconds" value="3600"/>
????????<property name="timeToLiveSeconds" value="3600"/>
????????<!-- 同ehcache參數(shù)maxElementsInMemory-->
???????<property name="maxEntriesLocalHeap"value="1000"/>
???????<!-- 同ehcache參數(shù)maxElementsOnDisk -->
????????<property name="maxEntriesLocalDisk" value="10000000"/>
????????<property name="memoryStoreEvictionPolicy" value="LRU"/>
????</cache>
1.4.5????加入ehcache的配置文件
在classpath下配置ehcache.xml
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
????xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">
????<diskStore path="F:\develop\ehcache"/>
????<defaultCache
maxElementsInMemory="1000"
???????maxElementsOnDisk="10000000"
???????eternal="false"
???????overflowToDisk="false"
???????timeToIdleSeconds="120"
???????timeToLiveSeconds="120"
???????diskExpiryThreadIntervalSeconds="120"
???????memoryStoreEvictionPolicy="LRU">
????</defaultCache>
</ehcache>
屬性說明:
? diskStore:指定數(shù)據(jù)在磁盤中的存儲位置。
? defaultCache:當(dāng)借助CacheManager.add("demoCache")創(chuàng)建Cache時,EhCache便會采用<defalutCache/>指定的的管理策略
以下屬性是必須的:
?maxElementsInMemory - 在內(nèi)存中緩存的element的最大數(shù)目
?maxElementsOnDisk - 在磁盤上緩存的element的最大數(shù)目,若是0表示無窮大
? eternal - 設(shè)定緩存的elements是否永遠(yuǎn)不過期。如果為true,則緩存的數(shù)據(jù)始終有效,如果為false那么還要根據(jù)timeToIdleSeconds,timeToLiveSeconds判斷
? overflowToDisk- 設(shè)定當(dāng)內(nèi)存緩存溢出的時候是否將過期的element緩存到磁盤上
以下屬性是可選的:
?timeToIdleSeconds - 當(dāng)緩存在EhCache中的數(shù)據(jù)前后兩次訪問的時間超過timeToIdleSeconds的屬性取值時,這些數(shù)據(jù)便會刪除,默認(rèn)值是0,也就是可閑置時間無窮大
?timeToLiveSeconds - 緩存element的有效生命期,默認(rèn)是0.,也就是element存活時間無窮大
???????diskSpoolBufferSizeMB 這個參數(shù)設(shè)置DiskStore(磁盤緩存)的緩存區(qū)大小.默認(rèn)是30MB.每個Cache都應(yīng)該有自己的一個緩沖區(qū).
?diskPersistent在VM重啟的時候是否啟用磁盤保存EhCache中的數(shù)據(jù),默認(rèn)是false。
?diskExpiryThreadIntervalSeconds - 磁盤緩存的清理線程運行間隔,默認(rèn)是120秒。每個120s,相應(yīng)的線程會進(jìn)行一次EhCache中數(shù)據(jù)的清理工作
?memoryStoreEvictionPolicy - 當(dāng)內(nèi)存緩存達(dá)到最大,有新的element加入的時候,移除緩存中element的策略。默認(rèn)是LRU(最近最少使用),可選的有LFU(最不常使用)和FIFO(先進(jìn)先出)
1.5??二級應(yīng)用場景
對于訪問多的查詢請求且用戶對查詢結(jié)果實時性要求不高,此時可采用mybatis二級緩存技術(shù)降低數(shù)據(jù)庫訪問量,提高訪問速度,業(yè)務(wù)場景比如:耗時較高的統(tǒng)計分析sql、電話賬單查詢sql等。
?????????實現(xiàn)方法如下:通過設(shè)置刷新間隔時間,由mybatis每隔一段時間自動清空緩存,根據(jù)數(shù)據(jù)變化頻率設(shè)置緩存刷新間隔flushInterval,比如設(shè)置為30分鐘、60分鐘、24小時等,根據(jù)需求而定。
1.6??二級緩存局限性
?????????mybatis二級緩存對細(xì)粒度的數(shù)據(jù)級別的緩存實現(xiàn)不好,對同時緩存較多條數(shù)據(jù)的緩存,比如如下需求:對商品信息進(jìn)行緩存,由于商品信息查詢訪問量大,但是要求用戶每次都能查詢最新的商品信息,此時如果使用mybatis的二級緩存就無法實現(xiàn)當(dāng)一個商品變化時只刷新該商品的緩存信息而不刷新其它商品的信息,因為mybaits的二級緩存區(qū)域以mapper為單位劃分,當(dāng)一個商品信息變化會將所有商品信息的緩存數(shù)據(jù)全部清空。解決此類問題需要在業(yè)務(wù)層根據(jù)需求對數(shù)據(jù)有針對性緩存。需要使用三級緩存
---------------------?
作者:雙斜杠少年?
來源:CSDN?
原文:https://blog.csdn.net/u012373815/article/details/47069223?
版權(quán)聲明:本文為博主原創(chuàng)文章,轉(zhuǎn)載請附上博文鏈接!
總結(jié)
以上是生活随笔為你收集整理的mybatis的缓存机制(一级缓存二级缓存和刷新缓存)和mybatis整合ehcache的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: vm模板引擎
- 下一篇: 《深入理解mybatis原理》 MyBa