摘要:緩存介紹正如大多數(shù)持久層框架一樣,同樣提供了一級(jí)緩存和二級(jí)緩存的支持一級(jí)緩存基于的本地緩存,其存儲(chǔ)作用域?yàn)?,?dāng)或之后,該中的所有就將清空。一級(jí)緩存實(shí)現(xiàn)對(duì)的操作內(nèi)部都是通過(guò)來(lái)執(zhí)行的。
MyBatis緩存介紹
正如大多數(shù)持久層框架一樣,MyBatis 同樣提供了一級(jí)緩存和二級(jí)緩存的支持
一級(jí)緩存: 基于PerpetualCache 的 HashMap本地緩存,其存儲(chǔ)作用域?yàn)?Session,當(dāng) Session flush 或 close 之后,該Session中的所有 Cache 就將清空。
二級(jí)緩存與一級(jí)緩存其機(jī)制相同,默認(rèn)也是采用 PerpetualCache,HashMap存儲(chǔ),不同在于其存儲(chǔ)作用域?yàn)? Mapper(Namespace),并且可自定義存儲(chǔ)源,如 Ehcache。
對(duì)于緩存數(shù)據(jù)更新機(jī)制,當(dāng)某一個(gè)作用域(一級(jí)緩存Session/二級(jí)緩存Namespaces)的進(jìn)行了 C/U/D 操作后,默認(rèn)該作用域下所有 select 中的緩存將被clear。
SqlSession : 代表和數(shù)據(jù)庫(kù)的一次會(huì)話,向用戶提供了操作數(shù)據(jù)庫(kù)的方法。
MappedStatement: 代表要發(fā)往數(shù)據(jù)庫(kù)執(zhí)行的指令,可以理解為是Sql的抽象表示。
Executor: 具體用來(lái)和數(shù)據(jù)庫(kù)交互的執(zhí)行器,接受MappedStatement作為參數(shù)。
映射接口: 在接口中會(huì)要執(zhí)行的Sql用一個(gè)方法來(lái)表示,具體的Sql寫在映射文件中。
映射文件: 可以理解為是Mybatis編寫Sql的地方,通常來(lái)說(shuō)每一張單表都會(huì)對(duì)應(yīng)著一個(gè)映射文件,在該文件中會(huì)定義Sql語(yǔ)句入?yún)⒑统鰠⒌男问健?/p>
MyBatis的一級(jí)查詢緩存(也叫作本地緩存)是基于org.apache.ibatis.cache.impl.PerpetualCache 類的 HashMap本地緩存,其作用域是SqlSession
在同一個(gè)SqlSession中兩次執(zhí)行相同的 sql 查詢語(yǔ)句,第一次執(zhí)行完畢后,會(huì)將查詢結(jié)果寫入到緩存中,第二次會(huì)從緩存中直接獲取數(shù)據(jù),而不再到數(shù)據(jù)庫(kù)中進(jìn)行查詢,這樣就減少了數(shù)據(jù)庫(kù)的訪問(wèn),從而提高查詢效率。
當(dāng)一個(gè) SqlSession 結(jié)束后,該 SqlSession 中的一級(jí)查詢緩存也就不存在了。
myBatis 默認(rèn)一級(jí)查詢緩存是開啟狀態(tài),且不能關(guān)閉。
增刪改會(huì)清空緩存,無(wú)論是否commit
當(dāng)SqlSession關(guān)閉和提交時(shí),會(huì)清空一級(jí)緩存
可能你會(huì)有疑惑,我的mybatis bean是由spring 來(lái)管理的,已經(jīng)屏蔽了sqlSession這個(gè)東西了?那怎么的一次操作才算是一次sqlSession呢?
spring整合mybatis后,非事務(wù)環(huán)境下,每次操作數(shù)據(jù)庫(kù)都使用新的sqlSession對(duì)象。因此mybatis的一級(jí)緩存無(wú)法使用(一級(jí)緩存針對(duì)同一個(gè)sqlsession有效)
在開啟事物的情況之下,spring使用threadLocal獲取當(dāng)前資源綁定同一個(gè)sqlSession,因此此時(shí)一級(jí)緩存是有效的
在開啟以及緩存的時(shí)候查詢得到的對(duì)象是同一個(gè)對(duì)象。
這種情況下會(huì)出現(xiàn)一個(gè)問(wèn)題。我們先看一下代碼。
public void listMybatisModel() { ListmybatisModels = mapper.listMybatisModel(); List mybatisModelsOther = mapper.listMybatisModel(); System.out.println(mybatisModels == mybatisModelsOther); System.out.println("list count: " + mybatisModels.size()); }
System.out.println(mybatisModels == mybatisModelsOther);
輸出結(jié)果竟然是true,這樣說(shuō)來(lái)是同一個(gè)對(duì)象。 會(huì)出現(xiàn)這種場(chǎng)景,第一次查出來(lái)的對(duì)象然后修改了,第二次查出來(lái)的就是修改后的對(duì)象。
對(duì)SqlSession的操作mybatis內(nèi)部都是通過(guò)Executor來(lái)執(zhí)行的。Executor的生命周期和SqlSession是一致的。Mybatis在Executor中創(chuàng)建了一級(jí)緩存,基于PerpetualCache 類的 HashMap
MyBatis的二級(jí)緩存是mapper范圍級(jí)別的
SqlSession關(guān)閉后才會(huì)將數(shù)據(jù)寫到二級(jí)緩存區(qū)域
增刪改操作,無(wú)論是否進(jìn)行提交commit(),均會(huì)清空一級(jí)、二級(jí)緩存
二級(jí)緩存是默認(rèn)開啟的。(想開啟就不必做任何配置)
二級(jí)緩存會(huì)使用 Least Recently Used (LRU,最近最少使用的)算法來(lái)收回。
根據(jù)時(shí)間表(如 no Flush Interval ,沒有刷新間隔),緩存不會(huì)以任何時(shí)間順序來(lái)刷新 。
緩存會(huì)存儲(chǔ)集合或?qū)ο螅o(wú)論查詢方法返回什么類型的值)的 1024 個(gè)引用。
緩存會(huì)被視為 read/write (可讀/可寫)的,意味著對(duì)象檢索不是共享的,而且可以安全地被調(diào)用者修改,而不干擾其他調(diào)用者或線程所做的潛在修改 。
用下面這張圖描述一級(jí)緩存和二級(jí)緩存的關(guān)系。
在保證二級(jí)緩存的全局配置開啟的情況下,給某個(gè)xml開啟二級(jí)緩存只需要在xml中添加
// mybatis-config.xml 中配置默認(rèn)值為 true。即二級(jí)緩存默認(rèn)是開啟的 // 具體mapper.xml 中配置
如果想要設(shè)置增刪改操作的時(shí)候不清空二級(jí)緩存的話,可以在其insert或delete或update中添加屬性flushCache=”false”,默認(rèn)為 true。
DELETE FROM user where id=#{id}
通過(guò)以下一些配置可以修改一些緩存參數(shù)。
配置創(chuàng)建了一個(gè)FIFO緩存,并每隔 60 秒刷新一次,存儲(chǔ)集合或?qū)ο蟮?12個(gè)引用, 而且返回的對(duì)象被認(rèn)為是只讀的,因此在不同線程中的調(diào)用者之間修改它們會(huì)導(dǎo)致沖突。cache可以配置的屬性如下。
eviction (收回策略)
LRU (最近最少使用的: 移除最長(zhǎng)時(shí)間不被使用的對(duì)象,這是默認(rèn)值 。
FIFO (先進(jìn)先出〉 : 按對(duì)象進(jìn)入緩存的順序來(lái)移除它們 。
SOFT (軟引用) : 移除基于垃圾回收器狀態(tài)和軟引用規(guī)則的對(duì)象 。
WEAK (弱引用) : 更積極地移除基于垃圾收集器狀態(tài)和弱引用規(guī)則的對(duì)象
flushinterval(刷新間隔)。
可以被設(shè)置為任意的正整數(shù),而且它們代表一個(gè)合理
的毫秒形式的時(shí)間段。默認(rèn)情況不設(shè)置,即沒有刷新間隔,緩存僅僅在調(diào)用語(yǔ)句時(shí)刷新。
size (引用數(shù)目)。 可以被設(shè)置為任意正整數(shù),要記住緩存的對(duì)象數(shù)目和運(yùn)行環(huán)境的可用內(nèi)存資源數(shù)目。默認(rèn)值是 1024 。
readOnly (只讀)。屬性可以被設(shè)置為 true 或 false 。只讀的緩存會(huì)給所有調(diào)用者
返回緩存對(duì)象的相同實(shí)例,因此這些對(duì)象不能被修改,這提供了很重要的性能優(yōu)勢(shì)??勺x寫的緩存會(huì)通過(guò)序列化返回緩存對(duì)象的拷貝,這種方式會(huì)慢一些,但是安全,因此默認(rèn)是false。
當(dāng)只使用注解方式配置二級(jí)緩存時(shí),如果在Mapper接口中,則需要增加如下配置 。
@CacheNamespace ( eviction = FifoCache.class , flushinterval = 60000 , size = 512 , readWrite = true) public interface Mapper { }
括號(hào)內(nèi)的內(nèi)容是配置緩存屬性。
Mapper 接口和對(duì)應(yīng)的 XML 文件是相同的命名空間,想使用二級(jí)緩存,兩者必須同時(shí)配置(如果接口不存在使用注解方式的方法,可以只在 XML 中配置〉,因此按照上面的方
式進(jìn)行配置就會(huì)出錯(cuò) , 這個(gè)時(shí)候應(yīng)該使用參照緩存。在 Mapper 接口中,參照緩存配置如下 。
@CacheNarnespaceRef(RoleMapper.class) public interface RoleMapper {
因?yàn)橄胱?RoleMapper 接口中的注解方法和 XML中的方法使用相同的緩存,因此使用參照緩存配置RoleMapper.class,這樣就會(huì)使用命名空間為xx.xxx.xxx.xxx.RoleMapper的緩存配置,即RoleMapper.xml 中配置的緩存 。
Mapper 接口可以通過(guò)注解引用XML 映射文件或者其他接口的緩存,在 XML 中也可以配置參照緩存,如可以在 RoleMapper.xml 中進(jìn)行如下修改 。
這樣配置后XML 就會(huì)引用 Mapper 接口中配置的二級(jí)緩存,同樣可以避免同時(shí)配置二級(jí)緩存導(dǎo)致的沖突。MyBatis 中很少會(huì)同時(shí)使用 Mapper 接口注解方式和XML映射文件,所以參照緩存并不是為了解決這個(gè)問(wèn)題而設(shè)計(jì)的。參照緩存除了能夠通過(guò)引用其他緩存減少配置外,主要的作用是解決臟讀。
MyBatis使用SerializedCache(org.apache.ibaits.cache.decorators.SerializedCache)序列化緩存來(lái)實(shí)現(xiàn)可讀寫緩存類,井通過(guò)序列化和反序列化來(lái)保證通過(guò)緩存獲取數(shù)據(jù)時(shí),得到的是一個(gè)新的實(shí)例。因此,如果配置為只讀緩存,MyBatis就會(huì)使用Map來(lái)存儲(chǔ)緩存值,這種情況下,從緩存中獲取的對(duì)象就是同一個(gè)實(shí)例。因?yàn)槭褂每勺x寫緩存,可以使用SerializedCache序列化緩存。這個(gè)緩存類要求所有被序列化的對(duì)象必須實(shí)現(xiàn) Serializable (java.io.Serializable)接口
雖然使用序列化得到的對(duì)象都是不一樣的對(duì)象修改時(shí)都是互不影響,但是還是不安全的。
Mybatis的二級(jí)緩存是和命名空間綁定的,所以通常情況下每一個(gè)Mapper映射文件都有自己的二級(jí)緩存,不同的mapper的二級(jí)緩存互不影響。
引起臟讀的操作通常發(fā)生在多表關(guān)聯(lián)操作中,比如在兩個(gè)不同的mapper中都涉及到同一個(gè)表的增刪改查操作,當(dāng)其中一個(gè)mapper對(duì)這張表進(jìn)行查詢操作,此時(shí)另一個(gè)mapper進(jìn)行了更新操作刷新緩存,然后第一個(gè)mapper又查詢了一次,那么這次查詢出的數(shù)據(jù)是臟數(shù)據(jù)。出現(xiàn)臟讀的原因是他們的操作的緩存并不是同一個(gè)。
mapper中的操作以單表操作為主,避免在關(guān)聯(lián)操作中使用mapper
使用參照緩存
緩存數(shù)據(jù)有內(nèi)存和磁盤兩級(jí),無(wú)須擔(dān)心容量問(wèn)題。
緩存數(shù)據(jù)會(huì)在虛擬機(jī)重啟的過(guò)程中寫入磁盤。可以通過(guò)RMI、可插入API等方式進(jìn)行分布式緩存。
具有緩存和緩存管理器的偵昕接口。
支持多緩存管理器實(shí)例以及一個(gè)實(shí)例的多個(gè)緩存區(qū)域。
org.mybatis.caches mybatis-ehcache 1.0.3
在 src/main/resources 目錄下新增 ehcache.xml 文件。
有關(guān)EhCache的詳細(xì)配置可以參考地址 http://www.ehcache.org/ehcach... 中的內(nèi)容。
copyOnRead 的含義是,判斷從緩存中讀取數(shù)據(jù)時(shí)是返回對(duì)象的引用還是復(fù)制一個(gè)對(duì)象返回。默認(rèn)情況下是false,即返回?cái)?shù)據(jù)的引用,這種情況下返回的都是相同的對(duì)象,和MyBatis默認(rèn)緩存中的只讀對(duì)象是相同的。如果設(shè)置為 true ,那就是可讀寫緩存,每次讀取緩存時(shí)都會(huì)復(fù)制一個(gè)新的實(shí)例 。
copyOnWrite 的含義是 ,判斷寫入緩存時(shí)是直接緩存對(duì)象的引用還是復(fù)制一個(gè)對(duì)象然后緩存,默認(rèn)也是false。如果想使用可讀寫緩存,就需要將這兩個(gè)屬性配置為true,如果使用只讀緩存,可以不配置這兩個(gè)屬性,使用默認(rèn)值 false 即可 。
修改Mapper.xml中的緩存配置
ehcache-cache 提供了如下 2 個(gè)可選的緩存實(shí)現(xiàn)。
org.mybatis.caches.ehcache.EhcacheCache
org.mybatis.caches.ehcache.LoggingEhcache 這個(gè)是帶日志的緩存。
在xml中添加
只通過(guò)設(shè)置 type 屬性就可 以使用 EhCache 緩存了,這時(shí)cache的其他屬性都不會(huì)起到任何作用,針對(duì)緩存的配置都在ehcache.xml中進(jìn)行。在ehcache.xml配置文件中,只有一個(gè)默認(rèn)的緩存配置,所以配置使用EhCache緩存的Mapper映射文件都會(huì)有一個(gè)以映射文件命名空間命名的緩存。如果想針對(duì)某一個(gè)命名空間進(jìn)行配置,需要在 ehcache.xml 中添加一個(gè)和映射文件命名空間一致的緩存配置,例如針對(duì)RoleMapper可以進(jìn)行如下配置。
添加依賴,目前只有bata版本。
org.mybatis.caches mybatis-redis 1.0.0-beta2
配置Redis
使用 Redis 前,必須有一個(gè) Redis 服務(wù),有關(guān)Redis安裝啟動(dòng)的相關(guān)內(nèi)容,可參考如下地址中的官方文檔:https://redis.io/topics/quick...。Redis服務(wù)啟動(dòng)后,在src/main/resources 目錄下新增 redis.properties 文件 。
host=localhost port=6379 connectionTimeout=SOOO soTimeout=SOOO password= database=O clientName=
修改mapper.xml中的配置。
〈 !一其他自己直一 〉
配置依然很簡(jiǎn)單, RedisCache 在保存緩存數(shù)據(jù)和獲取緩存數(shù)據(jù)時(shí),使用了Java的序列化和反序列化,因此還需要保證被緩存的對(duì)象必須實(shí)現(xiàn)Serializable接口。改為RedisCache緩存配置后, testL2Cache 測(cè)試第一次執(zhí)行時(shí)會(huì)全部成功,但是如果再次執(zhí)行,就會(huì)出錯(cuò)。這是因?yàn)镽edis作為緩存服務(wù)器,它緩存的數(shù)據(jù)和程序(或測(cè)試)的啟動(dòng)無(wú)關(guān),Redis 的緩存并不會(huì)因?yàn)閼?yīng)用的關(guān)閉而失效。所以再次執(zhí)行時(shí)沒有進(jìn)行一次數(shù)據(jù)庫(kù)查詢,所有查詢都使用緩存,測(cè)試的第一部分代碼中的rolel和role2都是直接從二級(jí)緩存中獲取數(shù)據(jù),因?yàn)槭强勺x寫緩存,所以不是相同的對(duì)象。當(dāng)需要分布式部署應(yīng)用時(shí),如果使用MyBatis自帶緩存或基礎(chǔ)的EhCahca緩存,分布式應(yīng)用會(huì)各自擁有自己的緩存,它們之間不會(huì)共享緩存 ,這種方式會(huì)消耗更多的服務(wù)器資源。如果使用類似 Redis 的緩存服務(wù),就可以將分布式應(yīng)用連接到同一個(gè)緩存服務(wù)器,實(shí)現(xiàn)分布式應(yīng)用間的緩存共享 。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/73058.html
摘要:一級(jí)緩存介紹及相關(guān)配置。在這個(gè)章節(jié),我們學(xué)習(xí)如何使用的一級(jí)緩存。一級(jí)緩存實(shí)驗(yàn)配置完畢后,通過(guò)實(shí)驗(yàn)的方式了解一級(jí)緩存的效果。源碼分析了解具體的工作流程后,我們隊(duì)查詢相關(guān)的核心類和一級(jí)緩存的源碼進(jìn)行走讀。 我,后端Java工程師,現(xiàn)在美團(tuán)點(diǎn)評(píng)工作。愛健身,愛技術(shù),也喜歡寫點(diǎn)文字。個(gè)人網(wǎng)站: http://kailuncen.me公眾號(hào): KailunTalk (凱倫說(shuō)) 前言 本文主要涉及...
摘要:一級(jí)緩存介紹及相關(guān)配置。在這個(gè)章節(jié),我們學(xué)習(xí)如何使用的一級(jí)緩存。一級(jí)緩存實(shí)驗(yàn)配置完畢后,通過(guò)實(shí)驗(yàn)的方式了解一級(jí)緩存的效果。源碼分析了解具體的工作流程后,我們隊(duì)查詢相關(guān)的核心類和一級(jí)緩存的源碼進(jìn)行走讀。 我,后端Java工程師,現(xiàn)在美團(tuán)點(diǎn)評(píng)工作。愛健身,愛技術(shù),也喜歡寫點(diǎn)文字。個(gè)人網(wǎng)站: http://kailuncen.me公眾號(hào): KailunTalk (凱倫說(shuō)) 前言 本文主要涉及...
摘要:前言本文介紹一種緩存的實(shí)現(xiàn)方法,使用實(shí)例如下通過(guò)使用注解來(lái)標(biāo)注哪些數(shù)據(jù)庫(kù)訪問(wèn)需要緩存,屬性設(shè)置前綴,這樣做的好處是將緩存的實(shí)現(xiàn)和業(yè)務(wù)邏輯分開,可擴(kuò)展性強(qiáng)實(shí)現(xiàn)如上所述,注解用于注釋需要緩存的接口方法在容器中通常都會(huì)配置一個(gè)用來(lái)指定的位置包名, 前言 本文介紹一種 mybatis redis 緩存的實(shí)現(xiàn)方法,使用實(shí)例如下: @Repository public interface User...
摘要:簡(jiǎn)介我從七月份開始閱讀源碼,并在隨后的天內(nèi)陸續(xù)更新了篇文章??紤]到超長(zhǎng)文章對(duì)讀者不太友好,以及拆分文章工作量也不小等問(wèn)題。經(jīng)過(guò)兩周緊張的排版,一本小小的源碼分析書誕生了。我在寫系列文章中,買了一本書作為參考,這本書是技術(shù)內(nèi)幕。 1.簡(jiǎn)介 我從七月份開始閱讀MyBatis源碼,并在隨后的40天內(nèi)陸續(xù)更新了7篇文章。起初,我只是打算通過(guò)博客的形式進(jìn)行分享。但在寫作的過(guò)程中,發(fā)現(xiàn)要分析的代碼...
閱讀 1411·2019-08-30 15:44
閱讀 2186·2019-08-30 11:04
閱讀 583·2019-08-29 15:17
閱讀 2609·2019-08-26 12:12
閱讀 3194·2019-08-23 18:09
閱讀 977·2019-08-23 15:37
閱讀 1611·2019-08-23 14:43
閱讀 2999·2019-08-23 13:13