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

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

為什么不推薦使用MyBatis二級緩存

jf_ro2CN3Fa ? 來源:芋道源碼 ? 2023-07-16 15:32 ? 次閱讀


為了增加查詢的性能,mybatis 提供了二級緩存架構,分為一級緩存和二級緩存。

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

一級緩存使用簡單,默認就開啟。二級緩存需要手動開啟,相對復雜,而且要注意的事項也多,否則可能有隱患。

一級緩存

應用場景

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

針對這種情況,mybatis通過一級緩存來解決:在同一次查詢會話(SqlSession)中如果出現(xiàn)相同的語句及參數(shù),就會從緩存中取出,不再走數(shù)據(jù)庫查詢。

一級緩存只能作用于查詢會話中,所以也叫做會話緩存。

生效的條件

一級緩存要生效,必須滿足以下條件條件:

  • 必須是相同的會話
  • 必須是同一個 mapper,即同一個 namespace
  • 必須是相同的 statement,即同一個 mapper 中的同一個方法
  • 必須是相同的 sql 和參數(shù)
  • 查詢語句中間沒有執(zhí)行 session.clearCache() 方法
  • 查詢語句中間沒有執(zhí)行 insert/update/delete 方法(無論變動記錄是否與緩存數(shù)據(jù)有無關系)

與springboot集成時一級緩存不生效原因

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

因為一級緩存是會話級別的,要生效的話,必須要在同一個 SqlSession 中。但是與 springboot 集成的 mybatis,默認每次執(zhí)行sql語句時,都會創(chuàng)建一個新的 SqlSession!所以一級緩存才沒有生效。

當調用 mapper 的方法時,最終會執(zhí)行到 SqlSessionUtilsgetSqlSession 方法,在這個方法中會嘗試在事務管理器中獲取 SqlSession,如果沒有開啟事務,那么就會 new 一個 DefaultSqlSession。

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

所以說,即便在同一個方法中,通過同一個 mapper 連續(xù)調用兩次相同的查詢方法,也不會觸發(fā)一級緩存。

解決與springboot集成時一級緩存不生效問題

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

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

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

沒錯,的確生效了。在代碼中可以看到,從事務管理器中,獲取到了 SqlSession:

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

再看看源碼中是什么時候將 SqlSession 設置到事務管理器中的。

SqlSessionUtils 中,在獲取到 SqlSession 后,會調用 registerSessionHolder 方法注冊 SessionHolder 到事務管理器:

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

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

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

然后在下次查詢時,就可以從這里取出此 SqlSession,使用同一個 SqlSession 查詢,一級緩存就生效了。

所以基本原理就是:如果當前線程存在事物,并且存在相關會話,就從 ThreadLocal 中取出。如果沒有事務,就重新創(chuàng)建一個 SqlSession 并存儲到 ThreadLocal 當中,共下次查詢使用。

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

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

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

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

二級緩存

應用場景

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

一級緩存針對的是同一個會話當中相同SQL,并不適合這情熱點數(shù)據(jù)的緩存場景。

為了解決這個問題引入了二級緩存,它脫離于會話之外,多個會話可以使用相同的緩存。

看一個例子:

@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));
}

}

當發(fā)送兩次 get 請求時(兩個不同的會話),通過日志可以發(fā)現(xiàn)第二次查詢使用的是緩存

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

開啟的方法

二級緩存需要手動來開啟,mybatis 默認沒有開啟二級緩存。

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

mybatis-plus:
configuration:
cache-enabled:true

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

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

3)實體類實現(xiàn) Serializable 接口

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

生效的條件

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

緩存清除條件

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

源碼中是如何填充二級緩存的?

在生效條件中提到了,二級緩存必須要在會話提交或關閉之后,才能生效!

在查詢到結果后,會調用 SqlSession 的 commit 方法進行提交(如果開啟事務的話,提交 SqlSession 走的不是這里了,但最終填充二級緩存的地方是一樣的。):

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

在此方法中,最終會調用到 TransactionalCacheflushPendingEntries 方法中填充二級緩存:

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

springboot 集成 mybatis 的話,如果沒有開啟事務,每次執(zhí)行查詢,都會創(chuàng)建新的 SqlSession,所以即使是在同一個方法中進行查詢操作,那也是跨會話的。

查詢時如何使用二級緩存?

在查詢的時候,最終會調用 MybatisCachingExecutor 的 query 方法,里面會從 TransactionalCacheManager 中嘗試根據(jù) key 獲取二級緩存的內容。

可以看到,這個 key 很長,由 mapper、調用的查詢方法、SQL 等信息拼接而成,這也是為什么想要二級緩存生效,必須滿足前面所說的條件。

如果能在二級緩存中查詢到,就直接返回了,不需要訪問數(shù)據(jù)庫。

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

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

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

打印日志是在 LoggingCache 中:

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

為什么mybatis默認不開啟二級緩存?

答案就是,不推薦使用二級緩存!

二級緩存雖然能帶來一定的好處,但是有很大的隱藏危害!

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

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

來看一個例子:

@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("====================查詢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("====================重新查詢PaymentVO====================");
ListvoList2=xxxMapper.getPaymentVO(1L);
System.out.println(JSON.toJSONString(voList2.get(0)));
}

上面的代碼,test()方法中前后兩次調用了 xxxMapper.getPaymentVO 方法,因為沒有加 @Transactional 注解,所以前后兩次查詢,是兩個不同的會話,第一次查詢完后,SqlSession 會自動 commit,所以二級緩存能夠生效;

然后在中間進行了 Item 表的更新操作,修改了下名稱;

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

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

執(zhí)行日志如下:

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

所以說,二級緩存的隱藏危害是比較大的,當有表關聯(lián)時,一個不注意就會出問題,不建議使用。


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

    關注

    1

    文章

    223

    瀏覽量

    26579
  • 管理器
    +關注

    關注

    0

    文章

    239

    瀏覽量

    18434
  • mybatis
    +關注

    關注

    0

    文章

    58

    瀏覽量

    6691

原文標題:為什么不推薦使用 MyBatis 二級緩存

文章出處:【微信號:芋道源碼,微信公眾號:芋道源碼】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏

    評論

    相關推薦

    CPU二級緩存打開器-加速

    不會給你最大的頻率的,所以可以超頻的,而這個軟件就是在超頻的情況下,打開CPU的二級緩存,充分發(fā)揮 CPU的工效!一定要搞清楚你的CPU二級緩存大小??!一定?。。。?nbsp; 問問別人或者直接看一下你的CPU說明書
    發(fā)表于 03-02 17:04

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

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

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

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

    二級緩存一致性

    裸機下運行多核程序,開128KB的二級緩存時,結果沒問題,但是開了256KB的二級緩存時,結果出現(xiàn)偏差,請問這種問題該怎么排查解決
    發(fā)表于 08-24 21:40

    CPU二級緩存容量

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

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

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

    什么是CPU一緩存/二級緩存?

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

    Mybatis緩存之一緩存

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

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

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

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

    提到二級緩存容量的差距,還得從兩大CPU巨頭對一緩存的理解說起。對,沒看錯,就是平常曝光率遠遜于二級緩存的“一
    的頭像 發(fā)表于 08-14 10:20 ?1.1w次閱讀

    如何檢測cpu二級緩存是否損壞 詳解二級緩存對CPU性能影響

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

    二級緩存怎么清理或者關閉二級緩存的辦法介紹

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

    二級緩存速度如何 二級緩存最大多少

    緩存大小也是CPU的重要指標之一,且緩存的結構和大小對CPU速度的影響大,CPU內緩存的運行頻率極高,一般是和處理器同頻運作,工作效率大于系統(tǒng)內存和硬盤。
    發(fā)表于 08-14 15:01 ?9857次閱讀

    二級緩存的簡單配置教程詳解 淺談二級緩存之功效

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

    mybatis緩存二級緩存的原理

    MyBatis是一種輕量級的持久化框架,它提供了一緩存二級緩存的機制來優(yōu)化數(shù)據(jù)庫操作性能。一緩存
    的頭像 發(fā)表于 12-03 11:55 ?887次閱讀