吴忠躺衫网络科技有限公司

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線(xiàn)課程
  • 觀看技術(shù)視頻
  • 寫(xiě)文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

為什么不推薦使用MyBatis二級(jí)緩存

jf_ro2CN3Fa ? 來(lái)源:芋道源碼 ? 2023-07-16 15:32 ? 次閱讀


為了增加查詢(xún)的性能,mybatis 提供了二級(jí)緩存架構(gòu),分為一級(jí)緩存和二級(jí)緩存。

這兩級(jí)緩存最大的區(qū)別就是:一級(jí)緩存是會(huì)話(huà)級(jí)別的,只要出了這個(gè) SqlSession,緩存就沒(méi)用了。而二級(jí)緩存可以跨會(huì)話(huà),多個(gè)會(huì)話(huà)可以使用相同的緩存!

一級(jí)緩存使用簡(jiǎn)單,默認(rèn)就開(kāi)啟。二級(jí)緩存需要手動(dòng)開(kāi)啟,相對(duì)復(fù)雜,而且要注意的事項(xiàng)也多,否則可能有隱患。

一級(jí)緩存

應(yīng)用場(chǎng)景

訂單表與會(huì)員表是存在一對(duì)多的關(guān)系,為了盡可能減少join查詢(xún),進(jìn)行了分階段查詢(xún)。即先查詢(xún)出訂單表,再根據(jù)member_id字段查詢(xún)出會(huì)員表,最后進(jìn)行數(shù)據(jù)整合。而如果訂單表中存在重復(fù)的member_id,就會(huì)出現(xiàn)很多重復(fù)查詢(xún)。

針對(duì)這種情況,mybatis通過(guò)一級(jí)緩存來(lái)解決:在同一次查詢(xún)會(huì)話(huà)(SqlSession)中如果出現(xiàn)相同的語(yǔ)句及參數(shù),就會(huì)從緩存中取出,不再走數(shù)據(jù)庫(kù)查詢(xún)。

一級(jí)緩存只能作用于查詢(xún)會(huì)話(huà)中,所以也叫做會(huì)話(huà)緩存。

生效的條件

一級(jí)緩存要生效,必須滿(mǎn)足以下條件條件:

  • 必須是相同的會(huì)話(huà)
  • 必須是同一個(gè) mapper,即同一個(gè) namespace
  • 必須是相同的 statement,即同一個(gè) mapper 中的同一個(gè)方法
  • 必須是相同的 sql 和參數(shù)
  • 查詢(xún)語(yǔ)句中間沒(méi)有執(zhí)行 session.clearCache() 方法
  • 查詢(xún)語(yǔ)句中間沒(méi)有執(zhí)行 insert/update/delete 方法(無(wú)論變動(dòng)記錄是否與緩存數(shù)據(jù)有無(wú)關(guān)系)

與springboot集成時(shí)一級(jí)緩存不生效原因

e2e30ca0-2388-11ee-962d-dac502259ad0.png

因?yàn)橐患?jí)緩存是會(huì)話(huà)級(jí)別的,要生效的話(huà),必須要在同一個(gè) SqlSession 中。但是與 springboot 集成的 mybatis,默認(rèn)每次執(zhí)行sql語(yǔ)句時(shí),都會(huì)創(chuàng)建一個(gè)新的 SqlSession!所以一級(jí)緩存才沒(méi)有生效。

當(dāng)調(diào)用 mapper 的方法時(shí),最終會(huì)執(zhí)行到 SqlSessionUtilsgetSqlSession 方法,在這個(gè)方法中會(huì)嘗試在事務(wù)管理器中獲取 SqlSession,如果沒(méi)有開(kāi)啟事務(wù),那么就會(huì) new 一個(gè) DefaultSqlSession

e3048fc4-2388-11ee-962d-dac502259ad0.png

所以說(shuō),即便在同一個(gè)方法中,通過(guò)同一個(gè) mapper 連續(xù)調(diào)用兩次相同的查詢(xún)方法,也不會(huì)觸發(fā)一級(jí)緩存。

解決與springboot集成時(shí)一級(jí)緩存不生效問(wèn)題

在上面的代碼中也看到了,mybatis 在查詢(xún)時(shí),會(huì)先從事務(wù)管理器中嘗試獲取 SqlSession,取不到才會(huì)去創(chuàng)建新的 SqlSession。所以可以猜測(cè)只要將方法開(kāi)啟事務(wù),那么一級(jí)緩存就會(huì)生效。

加上 @Transactional 注解,看下效果:

e3163832-2388-11ee-962d-dac502259ad0.png

沒(méi)錯(cuò),的確生效了。在代碼中可以看到,從事務(wù)管理器中,獲取到了 SqlSession:

e33ab5b8-2388-11ee-962d-dac502259ad0.png

再看看源碼中是什么時(shí)候?qū)?SqlSession 設(shè)置到事務(wù)管理器中的。

SqlSessionUtils 中,在獲取到 SqlSession 后,會(huì)調(diào)用 registerSessionHolder 方法注冊(cè) SessionHolder 到事務(wù)管理器:

e35705b0-2388-11ee-962d-dac502259ad0.png

具體是在 TransactionSynchronizationManagerbindResource 方法中操作的,將 SessionHolder 保存到線(xiàn)程本地變量(ThreadLocal) resources 中,這是每個(gè)線(xiàn)程獨(dú)享的。

e376d0a2-2388-11ee-962d-dac502259ad0.png

然后在下次查詢(xún)時(shí),就可以從這里取出此 SqlSession,使用同一個(gè) SqlSession 查詢(xún),一級(jí)緩存就生效了。

所以基本原理就是:如果當(dāng)前線(xiàn)程存在事物,并且存在相關(guān)會(huì)話(huà),就從 ThreadLocal 中取出。如果沒(méi)有事務(wù),就重新創(chuàng)建一個(gè) SqlSession 并存儲(chǔ)到 ThreadLocal 當(dāng)中,共下次查詢(xún)使用。

至于緩存查詢(xún)數(shù)據(jù)的地方,是在 BaseExecutor 中的 queryFromDatabase 方法中。執(zhí)行 doQuery 從數(shù)據(jù)庫(kù)中查詢(xún)數(shù)據(jù)后,會(huì)立馬緩存到 localCache(PerpetualCache類(lèi)型) 中:

e393ccd4-2388-11ee-962d-dac502259ad0.png

基于 Spring Boot + MyBatis Plus + Vue & Element 實(shí)現(xiàn)的后臺(tái)管理系統(tǒng) + 用戶(hù)小程序,支持 RBAC 動(dòng)態(tài)權(quán)限、多租戶(hù)、數(shù)據(jù)權(quán)限、工作流、三方登錄、支付、短信、商城等功能

  • 項(xiàng)目地址:https://github.com/YunaiV/ruoyi-vue-pro
  • 視頻教程:https://doc.iocoder.cn/video/

二級(jí)緩存

應(yīng)用場(chǎng)景

業(yè)務(wù)系統(tǒng)中存在很多的靜態(tài)數(shù)據(jù)如,字典表、菜單表、權(quán)限表等,這些數(shù)據(jù)的特性是不會(huì)輕易修改但又是查詢(xún)的熱點(diǎn)數(shù)據(jù)。

一級(jí)緩存針對(duì)的是同一個(gè)會(huì)話(huà)當(dāng)中相同SQL,并不適合這情熱點(diǎn)數(shù)據(jù)的緩存場(chǎng)景。

為了解決這個(gè)問(wèn)題引入了二級(jí)緩存,它脫離于會(huì)話(huà)之外,多個(gè)會(huì)話(huà)可以使用相同的緩存。

看一個(gè)例子:

@RestController
@RequestMapping("item")
publicclassItemController{

@Autowired
privateItemMapperitemMapper;

@GetMapping("/{id}")
publicvoidgetById(@PathVariable("id")Longid){
System.out.println("====================begin====================");
Itemitem=itemMapper.selectById(id);
System.out.println(JSON.toJSONString(item));
}

}

當(dāng)發(fā)送兩次 get 請(qǐng)求時(shí)(兩個(gè)不同的會(huì)話(huà)),通過(guò)日志可以發(fā)現(xiàn)第二次查詢(xún)使用的是緩存

e3ad30d4-2388-11ee-962d-dac502259ad0.png

開(kāi)啟的方法

二級(jí)緩存需要手動(dòng)來(lái)開(kāi)啟,mybatis 默認(rèn)沒(méi)有開(kāi)啟二級(jí)緩存。

1)在 yaml 中配置 cache-enabled 為 true

mybatis-plus:
configuration:
cache-enabled:true

2)Mapper 接口上添加 @CacheNamespace 注解

e3bec9c0-2388-11ee-962d-dac502259ad0.png

3)實(shí)體類(lèi)實(shí)現(xiàn) Serializable 接口

e3d060f4-2388-11ee-962d-dac502259ad0.png

生效的條件

  • 當(dāng)會(huì)話(huà)提交或關(guān)閉之后才會(huì)填充二級(jí)緩存
  • 必須是同一個(gè) mapper,即同一個(gè)命名空間
  • 必須是相同的 statement,即同一個(gè) mapper 中的同一個(gè)方法
  • 必須是相同的 SQL 語(yǔ)句和參數(shù)
  • 如果 readWrite=true(默認(rèn)就是true),實(shí)體對(duì)像必須實(shí)現(xiàn) Serializable 接口

緩存清除條件

  • 只有修改會(huì)話(huà)提交之后,才會(huì)執(zhí)行清空操作
  • xml 中配置的 update 不能清空 @CacheNamespace 中的緩存數(shù)據(jù)
  • 任何一種增刪改操作都會(huì)清空整個(gè) namespace 中的緩存

源碼中是如何填充二級(jí)緩存的?

在生效條件中提到了,二級(jí)緩存必須要在會(huì)話(huà)提交或關(guān)閉之后,才能生效!

在查詢(xún)到結(jié)果后,會(huì)調(diào)用 SqlSession 的 commit 方法進(jìn)行提交(如果開(kāi)啟事務(wù)的話(huà),提交 SqlSession 走的不是這里了,但最終填充二級(jí)緩存的地方是一樣的。):

e3e06576-2388-11ee-962d-dac502259ad0.png

在此方法中,最終會(huì)調(diào)用到 TransactionalCacheflushPendingEntries 方法中填充二級(jí)緩存:

e4081436-2388-11ee-962d-dac502259ad0.png

springboot 集成 mybatis 的話(huà),如果沒(méi)有開(kāi)啟事務(wù),每次執(zhí)行查詢(xún),都會(huì)創(chuàng)建新的 SqlSession,所以即使是在同一個(gè)方法中進(jìn)行查詢(xún)操作,那也是跨會(huì)話(huà)的。

查詢(xún)時(shí)如何使用二級(jí)緩存?

在查詢(xún)的時(shí)候,最終會(huì)調(diào)用 MybatisCachingExecutor 的 query 方法,里面會(huì)從 TransactionalCacheManager 中嘗試根據(jù) key 獲取二級(jí)緩存的內(nèi)容。

可以看到,這個(gè) key 很長(zhǎng),由 mapper、調(diào)用的查詢(xún)方法、SQL 等信息拼接而成,這也是為什么想要二級(jí)緩存生效,必須滿(mǎn)足前面所說(shuō)的條件。

如果能在二級(jí)緩存中查詢(xún)到,就直接返回了,不需要訪(fǎng)問(wèn)數(shù)據(jù)庫(kù)。

e4219e74-2388-11ee-962d-dac502259ad0.png

具體的調(diào)用層數(shù)實(shí)在太多,用到了裝飾者模式,最終是在 PerpetualCache 中獲取緩存的:

e43846f6-2388-11ee-962d-dac502259ad0.png

打印日志是在 LoggingCache 中:

e44f3a64-2388-11ee-962d-dac502259ad0.png

為什么mybatis默認(rèn)不開(kāi)啟二級(jí)緩存?

答案就是,不推薦使用二級(jí)緩存!

二級(jí)緩存雖然能帶來(lái)一定的好處,但是有很大的隱藏危害!

它的緩存是以 namespace(mapper) 為單位的,不同 namespace 下的操作互不影響。且 insert/update/delete 操作會(huì)清空所在 namespace 下的全部緩存。

那么問(wèn)題就出來(lái)了,假設(shè)現(xiàn)在有 ItemMapper 以及 XxxMapper,在 XxxMapper 中做了表關(guān)聯(lián)查詢(xún),且做了二級(jí)緩存。此時(shí)在 ItemMapper 中將 item 信息給刪了,由于不同 namespace 下的操作互不影響,XxxMapper 的二級(jí)緩存不會(huì)變,那之后再次通過(guò) XxxMapper 查詢(xún)的數(shù)據(jù)就不對(duì)了,非常危險(xiǎn)。

來(lái)看一個(gè)例子:

@Mapper
@Repository
@CacheNamespace
publicinterfaceXxxMapper{

@Select("selecti.iditemId,i.nameitemName,p.amount,p.unit_priceunitPrice"+
"fromitemiJOINpaymentponi.id=p.item_idwherei.id=#{id}")
ListgetPaymentVO(Longid);

}


@Autowired
privateXxxMapperxxxMapper;

@Test
voidtest(){
System.out.println("====================查詢(xún)PaymentVO====================");
ListvoList=xxxMapper.getPaymentVO(1L);
System.out.println(JSON.toJSONString(voList.get(0)));
System.out.println("====================更新item表的name====================");
Itemitem=itemMapper.selectById(1);
item.setName("java并發(fā)編程");
itemMapper.updateById(item);
System.out.println("====================重新查詢(xún)PaymentVO====================");
ListvoList2=xxxMapper.getPaymentVO(1L);
System.out.println(JSON.toJSONString(voList2.get(0)));
}

上面的代碼,test()方法中前后兩次調(diào)用了 xxxMapper.getPaymentVO 方法,因?yàn)闆](méi)有加 @Transactional 注解,所以前后兩次查詢(xún),是兩個(gè)不同的會(huì)話(huà),第一次查詢(xún)完后,SqlSession 會(huì)自動(dòng) commit,所以二級(jí)緩存能夠生效;

然后在中間進(jìn)行了 Item 表的更新操作,修改了下名稱(chēng);

由于 itemMapperxxxMapper 不是同一個(gè)命名空間,所以 itemMapper 執(zhí)行的更新操作不會(huì)影響到 xxxMapper 的二級(jí)緩存;

再次調(diào)用 xxxMapper.getPaymentVO,發(fā)現(xiàn)取出的值是走緩存的,itemName 還是老的。但實(shí)際上 itemName 在上面已經(jīng)被改了!

執(zhí)行日志如下:

e46a30f8-2388-11ee-962d-dac502259ad0.png

所以說(shuō),二級(jí)緩存的隱藏危害是比較大的,當(dāng)有表關(guān)聯(lián)時(shí),一個(gè)不注意就會(huì)出問(wèn)題,不建議使用。


聲明:本文內(nèi)容及配圖由入駐作者撰寫(xiě)或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • 緩存
    +關(guān)注

    關(guān)注

    1

    文章

    241

    瀏覽量

    26758
  • 管理器
    +關(guān)注

    關(guān)注

    0

    文章

    248

    瀏覽量

    18617
  • mybatis
    +關(guān)注

    關(guān)注

    0

    文章

    62

    瀏覽量

    6745

原文標(biāo)題:為什么不推薦使用 MyBatis 二級(jí)緩存

文章出處:【微信號(hào):芋道源碼,微信公眾號(hào):芋道源碼】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 人收藏

    評(píng)論

    相關(guān)推薦

    CPU二級(jí)緩存打開(kāi)器-加速

    不會(huì)給你最大的頻率的,所以可以超頻的,而這個(gè)軟件就是在超頻的情況下,打開(kāi)CPU的二級(jí)緩存,充分發(fā)揮 CPU的工效!一定要搞清楚你的CPU二級(jí)緩存大小啊!一定!!!!  問(wèn)問(wèn)別人或者直接看一下你的CPU說(shuō)明書(shū)
    發(fā)表于 03-02 17:04

    求大神指點(diǎn) 關(guān)于fpga實(shí)現(xiàn)數(shù)據(jù)的二級(jí)緩存

    如論文所示,應(yīng)該如何實(shí)現(xiàn)二級(jí)緩存處理呢?四路信號(hào)是有pi/4相位差的方波信號(hào)
    發(fā)表于 06-21 18:29

    Java程序員筆記之mybatis結(jié)合redis實(shí)戰(zhàn)二級(jí)緩存

    Java程序員筆記——mybatis結(jié)合redis實(shí)戰(zhàn)二級(jí)緩存
    發(fā)表于 06-10 09:15

    二級(jí)緩存一致性

    裸機(jī)下運(yùn)行多核程序,開(kāi)128KB的二級(jí)緩存時(shí),結(jié)果沒(méi)問(wèn)題,但是開(kāi)了256KB的二級(jí)緩存時(shí),結(jié)果出現(xiàn)偏差,請(qǐng)問(wèn)這種問(wèn)題該怎么排查解決
    發(fā)表于 08-24 21:40

    CPU二級(jí)緩存容量

    CPU二級(jí)緩存容量            CPU緩存(Cache Memory)是位
    發(fā)表于 12-24 10:22 ?541次閱讀

    什么是聯(lián)合并行處理二級(jí)緩存

    什么是聯(lián)合并行處理二級(jí)緩存? 聯(lián)合并行處理二級(jí)緩存是(set-associative)將二級(jí)緩存劃分不同的片段,在每一片段中包含許多緩存線(xiàn)
    發(fā)表于 02-04 10:34 ?320次閱讀

    什么是CPU一級(jí)緩存/二級(jí)緩存

    什么是CPU一級(jí)緩存/二級(jí)緩存?  即L1 Cache。集成在CPU內(nèi)部中,用于CPU在處理數(shù)據(jù)過(guò)程中數(shù)據(jù)的暫時(shí)保存。由于緩存指令和數(shù)據(jù)與CPU
    發(fā)表于 02-04 10:43 ?1191次閱讀

    Mybatis緩存之一級(jí)緩存

    本文主要講mybatis的一級(jí)緩存,一級(jí)緩存是SqlSession級(jí)別的緩存
    發(fā)表于 11-27 20:44 ?1250次閱讀
    <b class='flag-5'>Mybatis</b><b class='flag-5'>緩存</b>之一<b class='flag-5'>級(jí)</b><b class='flag-5'>緩存</b>

    二級(jí)緩存和三級(jí)緩存的區(qū)別

    我們所說(shuō)的電腦緩存是當(dāng)cpu在讀取數(shù)據(jù)的時(shí)候,首先是從緩存文件中進(jìn)行查找,然后找到之后會(huì)自動(dòng)讀取在輸入到cpu進(jìn)行處理,當(dāng)然如果沒(méi)有在緩存中找到對(duì)應(yīng)的緩存文件的話(huà),那么就會(huì)從內(nèi)存中讀取
    發(fā)表于 03-07 10:16 ?1w次閱讀
    <b class='flag-5'>二級(jí)緩存</b>和三<b class='flag-5'>級(jí)</b><b class='flag-5'>緩存</b>的區(qū)別

    怎樣啟用CPU的二級(jí)緩存 如何查看二級(jí)緩存的參數(shù)

    提到二級(jí)緩存容量的差距,還得從兩大CPU巨頭對(duì)一級(jí)緩存的理解說(shuō)起。對(duì),沒(méi)看錯(cuò),就是平常曝光率遠(yuǎn)遜于二級(jí)緩存的“一級(jí)
    的頭像 發(fā)表于 08-14 10:20 ?1.2w次閱讀

    如何檢測(cè)cpu二級(jí)緩存是否損壞 詳解二級(jí)緩存對(duì)CPU性能影響

    綜上所述,在CPU性能方面,并非只從二級(jí)緩存容量上作對(duì)比就可以得到準(zhǔn)確的答案,實(shí)際上還要考慮到緩存的總體設(shè)計(jì)結(jié)構(gòu)、一級(jí)數(shù) 據(jù)緩存容量等因素。
    發(fā)表于 08-14 10:39 ?1w次閱讀
    如何檢測(cè)cpu<b class='flag-5'>二級(jí)緩存</b>是否損壞 詳解<b class='flag-5'>二級(jí)緩存</b>對(duì)CPU性能影響

    二級(jí)緩存怎么清理或者關(guān)閉二級(jí)緩存的辦法介紹

    CPU緩存(Cache Memory)位于CPU與內(nèi)存之間的臨時(shí)存儲(chǔ)器,它的容量比內(nèi)存小但交換速度快。
    發(fā)表于 08-14 11:38 ?5872次閱讀
    <b class='flag-5'>二級(jí)緩存</b>怎么清理或者關(guān)閉<b class='flag-5'>二級(jí)緩存</b>的辦法介紹

    二級(jí)緩存速度如何 二級(jí)緩存最大多少

    緩存大小也是CPU的重要指標(biāo)之一,且緩存的結(jié)構(gòu)和大小對(duì)CPU速度的影響大,CPU內(nèi)緩存的運(yùn)行頻率極高,一般是和處理器同頻運(yùn)作,工作效率大于系統(tǒng)內(nèi)存和硬盤(pán)。
    發(fā)表于 08-14 15:01 ?1w次閱讀

    二級(jí)緩存的簡(jiǎn)單配置教程詳解 淺談二級(jí)緩存之功效

    緩存是指可以進(jìn)行高速數(shù)據(jù)交換的存儲(chǔ)器,它先于內(nèi)存與CPU交換數(shù)據(jù),因此速度很快。L1Cache(一級(jí)緩存)是CPU第一層高速緩存
    發(fā)表于 08-14 15:28 ?4344次閱讀
    <b class='flag-5'>二級(jí)緩存</b>的簡(jiǎn)單配置教程詳解 淺談<b class='flag-5'>二級(jí)緩存</b>之功效

    mybatis級(jí)緩存二級(jí)緩存的原理

    MyBatis是一種輕量級(jí)的持久化框架,它提供了一級(jí)緩存二級(jí)緩存的機(jī)制來(lái)優(yōu)化數(shù)據(jù)庫(kù)操作性能。一級(jí)緩存
    的頭像 發(fā)表于 12-03 11:55 ?1241次閱讀
    百家乐官网庄闲符号记| 百家乐官网游戏奥秘| 全讯网开奖直播| 百家乐注册开户送现金| 基础百家乐官网博牌| 富二代百家乐官网的玩法技巧和规则| 破解百家乐官网打路单| 百家乐官网怎么出千| 百家乐官网龙虎扑克| 百家乐官网免费改| 最新百家乐官网的玩法技巧和规则 | 百家乐博送彩金18| 百家乐投注方法| 大发888登陆器下载| K7百家乐的玩法技巧和规则| 百家乐知识技巧玩法| 百家乐作弊知识| 蓝盾百家乐具体玩法| 墨尔本百家乐的玩法技巧和规则 | A8百家乐娱乐场| 大发888蜜月旅行| 娱网棋牌| 博久百家乐官网论坛| 专业百家乐官网筹码| 百家乐官网赌博娱乐城| 澳门百家乐必胜| 摩纳哥百家乐官网娱乐城| 百家乐统计概率| 网上百家乐娱乐场开户注册| 百家乐如何投注| 鑫鑫百家乐的玩法技巧和规则 | 百家乐足球投注网哪个平台网址测速最好 | 百家乐官网牌壳| 百家乐赌博千术| 任你博娱乐| 百家乐官网下对子的概率| 百家乐官网技巧打| 自贡百家乐娱乐场开户注册| 百家乐官网游戏出售| 菲律宾百家乐官网娱乐平台| 百家乐官方游戏|