MyBatis教程[6]----一級緩存、二級緩存

1. 爲什麼要使用緩存

針對頻繁被查詢的數據,將這部分數據放到內存中,這樣的好處是增加查詢速度(畢竟從內存中直接拿數據比從磁盤中讀數據庫快多了),減輕數據庫(磁盤)壓力。緩存應該使用在讀操作多於寫操作的應用場景中,當然如果系統裏的寫操作居多,也沒有必要使用緩存。緩存的存在可以提高系統的併發性能,提高響應速度。

2. 一級緩存(本地會話緩存/Session緩存)

默認情況下,只啓用了本地的會話緩存,它僅僅對一個會話中的數據進行緩存。 Mybatis默認支持一級緩存,不需要我們單獨對其進行配置。

Session緩存在操作數據時有以下邏輯:

  • 查詢數據邏輯:
    • 根據SQL查詢語句生成一個key
    • 使用生成的key值在當前Session的緩存中(Map)查找
    • 如果查到了,則直接返回查詢結果
    • 如果沒有查到,則去數據庫中查詢數據,並將key值、查詢結果以key-value形式寫到緩存中
  • 數據變更邏輯:
    • insert、update 和 delete 語句會刷新緩存

緩存使用的存儲結構是Map:

  • key:hashcode+sql+sql輸入參數+輸出參數(sql的唯一標識)

  • value:數據信息

針對同一個Session而言,如果前後兩次執行相同的SQL,並且中間沒有增刪改數據,則第二次查詢數據是可以直接從緩存中查詢出來的,不需要去查詢數據庫。

需要注意的是:Mybatis在整合了Spring後,在沒有開啓事務的情況下一級緩存會失效。具體原因可查看博客

3. 二級緩存(Mapper緩存)

前面我們已經把一級緩存瞭解了一下,接下來說一說二級緩存。二級緩存的作用域是Mapper級別的。換句話說,二級緩存的作用域是同一namespace。
在這裏插入圖片描述
二級緩存具有以下特點:

  • 映射語句文件中的所有 select 語句的結果將會被緩存。
  • 映射語句文件中的所有 insert、update 和 delete 語句會刷新緩存。
  • 緩存會使用最近最少使用算法(LRU, Least Recently Used)算法來清除不需要的緩存。
  • 緩存不會定時進行刷新(也就是說,沒有刷新間隔)。
  • 緩存會保存列表或對象(無論查詢方法返回哪種)的 1024 個引用。
  • 緩存會被視爲讀/寫緩存,這意味着獲取到的對象並不是共享的,可以安全地被調用者修改,而不干擾其他調用者或線程所做的潛在修改。

二級緩存的開啓也很簡單,只需要在映射文件中加一個標籤即可:

!--namespace對應Mapper接口的全類名,這樣就可以自動匹配上-->
<mapper namespace="com.yky.springboot.mapper.UserMapper">
    <cache/>
    .....
</mapper>

如果想要修改其默認屬性,可以這樣做:

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

這個更高級的配置創建了一個 FIFO 緩存,每隔 60 秒刷新,最多可以存儲結果對象或列表的 512 個引用,而且返回的對象被認爲是隻讀的,因此對它們進行修改可能會在不同線程中的調用者產生衝突。

可用的清除策略有:

  • LRU – 最近最少使用:移除最長時間不被使用的對象。
  • FIFO – 先進先出:按對象進入緩存的順序來移除它們。
  • SOFT – 軟引用:基於垃圾回收器狀態和軟引用規則移除對象。
  • WEAK – 弱引用:更積極地基於垃圾收集器狀態和弱引用規則移除對象。
    默認的清除策略是 LRU。

flushInterval(刷新間隔)屬性可以被設置爲任意的正整數,設置的值應該是一個以毫秒爲單位的合理時間量。 默認情況是不設置,也就是沒有刷新間隔,緩存僅僅會在調用語句時刷新。

size(引用數目)屬性可以被設置爲任意正整數,要注意欲緩存對象的大小和運行環境中可用的內存資源。默認值是 1024。

readOnly(只讀)屬性可以被設置爲 true 或 false。只讀的緩存會給所有調用者返回緩存對象的相同實例。 因此這些對象不能被修改。這就提供了可觀的性能提升。而可讀寫的緩存會(通過序列化)返回緩存對象的拷貝。 速度上會慢一些,但是更安全,因此默認值是 false。

4. 二級緩存實戰

單元測試代碼:

    @Test
    void selectById() {

        User user = userMapper.selectById(1L);
        userMapper.selectById(1L);
        userMapper.selectById(1L);
        userMapper.selectById(1L);
        System.out.println(user);
    }

開啓二級緩存:
在這裏插入圖片描述
可以看到,程序實際上只向數據庫發送了一條SQL語句,剩下那兩次查詢直接從緩存中讀取的。

關閉二級緩存:
在這裏插入圖片描述
可以看到,程序向數據庫發送了三條SQL語句。

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