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