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語句。