MyBatis驗(yàn)證多級(jí)緩存及 Cache Aside 模式的應(yīng)用小結(jié)
前言
MyBatis 官方文檔 中文版本沒有翻譯cache的部分,網(wǎng)上資料比較雜。
這里使用 Spock 測(cè)試框架驗(yàn)證下多級(jí)緩存,并探索 Mybatis 的 CacheAside 模式。注意:
- 本文用 本地緩存 表示一級(jí)緩存,全局緩存 表示二級(jí)緩存
- 用例倉庫
1. 多級(jí)緩存的概念
多級(jí)緩存可以聯(lián)系CPU的結(jié)構(gòu),離核心約近的一致性越高。
1.1 CPU 多級(jí)緩存

1.2 MyBatis 多級(jí)緩存
本地緩存默認(rèn)開啟,全局緩存需要使用 <cache/> 開啟
By default, just local session caching is enabled that is used solely to cache data for the duration of a session. To enable a global second level of caching you simply need to add one line to your SQL Mapping file:
<mapper namespace="com.james.mapper.FileCacheMapper">
<cache/> <!-- 聲明該標(biāo)簽,全局緩存開啟 -->
<select id="select" resultType="java.lang.String">
SELECT file_name FROM file
</select>
</mapper>2. MyBatis 本地緩存
用 Spring 注入的 mapper,調(diào)用一次select方法就會(huì)產(chǎn)生一個(gè) sqlSession,沒有利用到本地緩存。
def "未使用事務(wù),第二次查詢,不命中本地緩存"() {
given:
def list1 = mapper.select()
def list2 = mapper.select()
expect:
list1 !== list2
}用事務(wù)包裹后,兩次 select 共用一個(gè) sqlSession,緩存命中
def "使用事務(wù),命中緩存"() {
given:
def list1 = []
def list2 = []
when:
transaction.execute {
list1 = mapper.select()
list2 = mapper.select()
}
then:
// 同一個(gè)事務(wù)使用同一個(gè)SqlSession,若引用相同則認(rèn)為命中緩存
list1 === list2
}note: groovy 中
list1 === list2表示引用相同,list1 == list2表示兩個(gè)列表的內(nèi)容相同
3. MyBatis 全局緩存
上文說到,沒有事務(wù)保護(hù)的 select方法調(diào)用無法公用一個(gè) sqlSession,所以利用不了本地緩存。
全局緩存的范圍更大,只要是同一個(gè)mapper的調(diào)用,都會(huì)被緩存。
def "全局緩存默認(rèn)關(guān)閉,需要在xml文件中使用 <cache/> 標(biāo)簽啟用"() {
given:
def list1 = fileCacheMapper.select()
def list2 = fileCacheMapper.select()
expect:
// 由于 SerializedCache.java:64 使用的是由byte[]序列化方式存儲(chǔ)元素,所以實(shí)例的地址必然不同
list1 !== list2
list1 == list2
}3.1 MyBatis 全局緩存過期算法

值得關(guān)注的是 SOFT 和 WEAK 的類型,對(duì)應(yīng)Java中軟引用和弱引用。
軟引用是在內(nèi)存不足時(shí)GC可以回收,弱引用是下次GC即可回收(比軟引用)積極。
3.2 CacheAside 模式
以下是 Mybatis 默認(rèn)的全局緩存失效模式,也就是 Cache Aside 模式的應(yīng)用。
- 查詢的時(shí)候,如果沒有緩存,則寫入。
- 任何數(shù)據(jù)操作,使緩存失效。
<select ... flushCache="false" useCache="true"/> <insert ... flushCache="true"/> <update ... flushCache="true"/> <delete ... flushCache="true"/>
后記
Cache Aside 并不能保證強(qiáng)一致性,不然也就不會(huì)有 Paxos 這種復(fù)雜的共識(shí)算法了。 —— 《鳳凰架構(gòu)》
MyBatis 提供了緩存切口, 采用 Redis 會(huì)引入什么問題?
- 多實(shí)例之間緩存重復(fù)的失效問題,查詢時(shí)競(jìng)爭(zhēng)寫緩存的問題。
- ORM框架與中間件耦合,違反單一職責(zé)。
萬一遇到需強(qiáng)一致場(chǎng)景,如何增強(qiáng)?
兩個(gè)查詢請(qǐng)求同時(shí)到來,此時(shí)緩存為空,需要將MySql數(shù)據(jù)寫入緩存。此時(shí)會(huì)出現(xiàn)競(jìng)爭(zhēng)寫緩存的情況。用寫鎖來保證緩存內(nèi)的數(shù)據(jù)跟數(shù)據(jù)庫保持一致。
public void query() {
if (cache 命中) {
retrun cache
}
獲取緩存寫鎖
if (獲取鎖失敗) {
return 查數(shù)據(jù)庫
}
查數(shù)據(jù)庫
寫緩存
釋放緩存寫鎖
}到此這篇關(guān)于MyBatis驗(yàn)證多級(jí)緩存及 Cache Aside 模式的應(yīng)用小結(jié)的文章就介紹到這了,更多相關(guān)MyBatis驗(yàn)證多級(jí)緩存內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Spring 靜態(tài)變量/構(gòu)造函數(shù)注入失敗的解決方案
我們經(jīng)常會(huì)遇到一下問題:Spring對(duì)靜態(tài)變量的注入為空、在構(gòu)造函數(shù)中使用Spring容器中的Bean對(duì)象,得到的結(jié)果為空。不要擔(dān)心,本文將為大家介紹如何解決這些問題,跟隨小編來看看吧2021-11-11
RocketMQ 5.x + Spring Boot 發(fā)送消息失敗全解
本文給大家介紹RocketMQ 5.x + Spring Boot 發(fā)送消息失敗全解析,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧2026-02-02
java模擬TCP通信實(shí)現(xiàn)客戶端上傳文件到服務(wù)器端
這篇文章主要為大家詳細(xì)介紹了java模擬TCP通信實(shí)現(xiàn)客戶端上傳文件到服務(wù)器端,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-10-10
SpringBoot或SpringAI對(duì)接DeepSeek大模型的詳細(xì)步驟
這篇文章主要介紹了DeepSeek智能助手的使用方法和步驟,包括引入庫、配置環(huán)境變量和配置,文章詳細(xì)描述了流式請(qǐng)求和非流式請(qǐng)求的實(shí)現(xiàn)方式,需要的朋友可以參考下2025-02-02
由@NotNull注解引出的關(guān)于Java空指針的控制
這是一些很容易學(xué)會(huì)的簡(jiǎn)單技術(shù),但是對(duì)于代碼質(zhì)量和健壯性來說確實(shí)很重要。以我的經(jīng)驗(yàn),僅是第一個(gè)小技巧就已經(jīng)對(duì)改進(jìn)代碼質(zhì)量具有很大的作用了2016-09-09
解決java 分割字符串成數(shù)組時(shí),小圓點(diǎn)不能直接進(jìn)行分割的問題
這篇文章主要介紹了解決java 分割字符串成數(shù)組時(shí),小圓點(diǎn)不能直接進(jìn)行分割的問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2020-12-12
spring boot下mybatis配置雙數(shù)據(jù)源的實(shí)例
這篇文章主要介紹了spring boot下mybatis配置雙數(shù)據(jù)源的實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-09-09

