Mybatis-Mybatis緩存機制&Mybatis動態代理機制

Mybatis緩存機制:

我們一般都會使用mybatis的默認緩存配置,但是mybatis的緩存機制有不足之處,使用中可能會造成髒數據問題。

Mybatis一級緩存:

mybatis一級緩存是sqlsession級別的。一級緩存的作用域是一個sqlsession。mbatis默認開啓一級緩存
在同一個sqlsession中,執行相同的查詢sql。第一次會去查詢數據庫,並寫道緩存中,第二次直接從緩存中讀取。若對應數據發生增刪改操作,則緩存失效。
在這裏插入圖片描述
一級緩存是以sqlsession爲單位進行劃分的,若找不到在去數據庫查詢,然後將結果寫到緩存中。mybatis內部使用HashMap ,key爲hashcode+statementid+sql語句。value爲查詢出來的結果集映射成的java對象。

一級緩存帶來的髒數據問題:

當使用兩個或者兩個以上的sqlsession時,由於緩存數據是sqlsession內共享的,當另一個sqlsession進行增刪改操作時,其它sqlsession時感知不到,因此其此時緩存中的數據編程髒數據。

@Test
public void testLocalCacheScope() throws Exception {
        SqlSession sqlSession1 = factory.openSession(true); 
        SqlSession sqlSession2 = factory.openSession(true); 
 
        StudentMapper studentMapper = sqlSession1.getMapper(StudentMapper.class);
        StudentMapper studentMapper2 = sqlSession2.getMapper(StudentMapper.class);
 
        System.out.println("studentMapper讀取數據: " + studentMapper.getStudentById(1));
        System.out.println("studentMapper讀取數據: " + studentMapper.getStudentById(1));
        System.out.println("studentMapper2更新了" + studentMapper2.updateStudentName("小岑",1) + "個學生的數據");
        System.out.println("studentMapper讀取數據: " + studentMapper.getStudentById(1));
        System.out.println("studentMapper2讀取數據: " + studentMapper2.getStudentById(1));
}

在這裏插入圖片描述

一級緩存總結:
  1. mybatis一級緩存聲明週期和sqlsession一致。
  2. mybatis緩存內部設計簡單,只有一個沒有容量限制的hashmap,緩存性能有欠缺/
  3. mybatis一級緩存的最大範圍是sqlsession內部,在多個sqlsession或者分佈式環境下,數據庫寫操作會引起髒數據問題

Mybatis二級緩存:

mybatis二級緩存指mapper映射文件。二級緩存的作用域是同一個namespace(命名空間)下的mapper映射文件內容。多個sqlsession共享。
Mybatis需要手動設置二級緩存
同一個namespace下的mapper文件中,執行相同的sql查詢語句,第一次會讀取數據庫,並寫道緩存中,若中間沒有進行增刪改操作,則從緩存讀取查詢結果。
在這裏插入圖片描述

配置二級緩存:
  • 在全局配置文件中進行如下配置
<settings>
    <!-- 打開延遲加載的開關 -->
    <setting name="lazyLoadingEnabled" value="true"/>
    <!-- 將積極加載改爲消極加載,即延遲加載 -->
    <setting name="aggressiveLazyLoading" value="false"/>
     <!--開啓二級緩存-->
     <setting name="cacheEnabled" value="true"/>
</settings>

  • 在對應mapper下進行如下配置:
  1. cache可配置參數
type:cache使用的類型,默認是PerpetualCache,這在一級緩存中提到過。
 
eviction: 定義回收的策略,常見的有FIFO,LRU。
 
flushInterval: 配置一定時間自動刷新緩存,單位是毫秒。
 
size: 最多緩存對象的個數。
 
readOnly: 是否只讀,若配置可讀寫,則需要對應的實體類能夠序列化。
 
blocking: 若緩存中找不到對應的key,是否會一直blocking,直到有對應的數據進入緩存。
  1. 映射的java對象實現序列化

  2. 映射文件對應的需要開啓二級緩存的namespace進行如下設置

<cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"></cache>

<!--cache-ref 代表引用別的namespace中的Cache配置,兩個namespace使用同一個Cache-->
<cache-ref namespace="mapper.StudentMapper"/>
 
 //一二級緩存均有用
select 標籤下:user  userCache標籤 是否使用緩存
update/selete/update 標籤下  : flushCache標籤  刷新緩存
注意: MyBatis的二級緩存不適應用於映射文件中存在多表查詢的情況。

通常我們會爲每個單表創建單獨的映射文件,由於MyBatis的二級緩存是基於namespace的,多表查詢語句所在的namspace無法感應到其他namespace中的語句對多表查詢中涉及的表進行的修改,引發髒數據問題。
可以使用Cache-ref,這樣兩個映射文件對應的Sql操作都使用的是同一塊緩存了。
不過這樣做的後果是,緩存的粒度變粗了,多個Mapper namespace下的所有操作都會對緩存使用造成影響。

二級緩存總結:
  1. 二級緩存相對於一級緩存來說,實現了sqlsession間的緩存數據共享,粒度到namespace級別,
  2. mybatis在進行多表查詢時,極大可能會出現髒數據,有設計上缺陷,安全使用二級緩存的條件比較苛刻。
  3. 在分佈式環境下,由於默認的mybatis cache都是基於本地的,分佈式環境下必然會出現髒數據問題(多個server服務器都會進行緩存,且互不通知數據更新),需要使用集中式緩存將mybatis的cache接口實現,有一定的開發成本。相比較下,直接使用redis等分佈式緩存可能成本更低,安全性更高。
建議在生產環境下關閉mybatis緩存,只作爲一個ORM(對象關係映射)框架

Mybatis動態代理機制:

參考博客:

美團技術聊聊MyBatis緩存機制

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章