什么是快取? 為什么使用快取? 什么場景下使用快取?
快取(Cache)就是資料交換的緩沖區,一個臨時存盤資料的地方,當我們讀取資料時會首先從快取中查找需要的資料,如果找到了則直接執行,找不到的話再從記憶體中找,
在實際開發中,我們會經常對資料庫進行資料查詢,而從資料庫讀取資料的效率是非常低下的,并且頻繁地去訪問資料庫會增大資料庫壓力降低資料庫查詢性能等,所以我們可以將經常查詢且不經常改變的資料保存到快取中(快取就是記憶體中的一個物件),這樣用戶在查詢的時候就不用到資料庫中查詢(磁盤),從而減少與資料庫的交付次數,從而提高查詢效率,解決了高并發系統的性能問題,
快取的本質就是用空間換時間,犧牲資料的實時性,以服務器記憶體中的資料暫時代替從資料庫讀取最新的資料,減少資料庫IO,減輕服務器壓力,減少網路延遲,從而提高訪問速度,
Mybatis的快取機制
Mybatis一級快取(sqlSession級別)
? 一級快取是SqlSession級別的快取,在操作資料庫時需要構造SqlSession物件,在物件中有一個 資料結構(HashMap) 用于存盤快取資料,不同的SqlSession之間的快取資料區域(HashMap)互不影響,
? 當在同一個sqlSession (會話) 中執行兩次相同的SQL陳述句時,第一次執行完畢會將從資料庫中查詢的資料寫到快取(記憶體),第二次查詢時會從快取中獲取資料,不再去底層資料庫查詢,從而提高查詢效率,需要注意的是,如果sqlSession執行了DML操作(insert、update、delete),并提交到資料庫,MyBatis則會清空sqlSession中的一級快取,這樣做的目的是為了保證快取中存盤的是最新的資訊,避免出現臟讀現象,
MyBatis默認開啟一級快取,不需要進行任何配置,當一個sqlSession結束后該sqlSession的一級快取也就不在了,一級快取是不能關閉的,
測驗說明:
? 我們可以創建一張學生表,寫sql查詢陳述句根據id查詢學生資訊,定義一個方法,在方法內呼叫三次該查詢,提前開啟日志列印方便我們在控制臺查看列印的sql陳述句,我們可以看到,只有第一次列印了sql陳述句也就是真正查詢了資料庫,后面的查詢使用了一級快取,直接在快取中讀取的資料并沒有訪問資料庫,
? 我們接著對上面的資料進行測驗,從上面我們可以知道學生表中的資料已經存入到快取中,接下來我們可以對資料進行(增/刪/改)測驗(insert、update、delete),再進行查詢,可以發現進行了增、刪、改操作后控制臺列印了后面的查詢sql陳述句,也就是再次訪問了資料庫進行查詢,所以清空了一級快取導致失效了,
? 我們繼續測驗,這次我們開啟兩個SqlSession(會話),在SqlSession1中我們進行查詢操作從而開啟一級快取,在SqlSession2我們可以進行(增/刪/改)操作,再用SqlSession1去查詢,可以發現出現了臟資料,SqlSession1并沒有查詢到SqlSession2修改后的資料,所以驗證了一級快取只在資料庫會話內部共享
小結:
-
一級快取(本地快取), 作用域默認為sqlSession,當 Session flush 或 close 后, 該Session 中的所有Cache 將被清空,
-
本地快取不能被關閉, 但可以呼叫clearCache()來清空本地快取, 或者改變快取的作用域,
-
在mybatis3.1之后,可以配置本地快取的作用域,在 mybatis.xml 中配置,
-
讓一級快取失效的幾種情況:
① 不同的SqlSession對應不同的一級快取
② 同一個SqlSession但是查詢條件不同
③ 同一個SqlSession的兩次查詢期間執行了增刪改操作
④ 同一個SqlSession的兩次查詢期間手動清空了快取
Mybatis二級快取
二級快取也叫全域快取,一級快取作用域太低了,二級快取默認是全域開啟的,它是基于namespace級別的快取,一個名稱空間,對應一個二級快取,所以也稱之為“namespace快取”,需要在配置SQL陳述句的XML中添加節點, 以表示當前XML中的所有查詢都允許開通二級快取,并且,在節點上配置useCache=“true”,則對應的節點的查詢結果將被二級快取處理,并且,此查詢回傳的結果的型別必須是實作了Serializable介面的,如果使用了配置如何封裝查詢結果,則必須使用節點來封裝主鍵的映射,滿足以上條件后,二級快取將可用,只要是當前namespace中查詢出來的結果,都會根據所執行的SQL陳述句及引數進行 結果的快取
- 開啟二級快取后,會使用CachingExecutor裝飾Executor,進入一級快取的查詢流程前,先在CachingExecutor進行二級快取的查詢,
- 二級快取開啟后,同一個namespace下的所有操作陳述句,都影響著同一個Cache,即二級快取被多個SqlSession共享,是一個全域的變數,
- 當開啟快取后,資料的查詢執行的流程就是 二級快取 -> 一級快取 -> 資料庫,
開啟二級快取具體步驟:
-
在mybatis-config.xml檔案中開啟快取
<setting name="cacheEnabled"value="https://www.cnblogs.com/wren/p/true"/> <!-- 全域配置引數,需要時再設定 --> <settings> <!-- 開啟二級快取 默認值為true --> <setting name="cacheEnabled" value="https://www.cnblogs.com/wren/p/true"/> </settings> -
在mapper.xml組態檔中使用二級快取
- type:cache使用的型別,默認是PerpetualCache,這在一級快取中提到過,
- eviction: 定義回收的策略,常見的有FIFO,LRU,
- flushInterval: 配置一定時間自動重繪快取,單位是毫秒,
- size: 最多快取物件的個數,
- readOnly: 是否只讀,若配置可讀寫,則需要對應的物體類能夠序列化,
- blocking: 若快取中找不到對應的key,是否會一直blocking,直到有對應的資料進入快取,
<!--在當前Mapper.xml檔案中使用二級快取--> <mapper namespace="cn.hpu.mybatis.mapper.UserMapper"> <!-- 開啟本mapper namespace下的二級快取 --> <cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/> <cache/>
也可以直接在mapper.xml檔案中加入,但是要記得物體類要序列化,不然容易會報Caused by: java.io.NotSerializableException: com.xsq.pojo.User例外
-
在物體類中實作序列化:
public class User implements Serializable { //Serializable實作序列化,為了將來反序列化 }
作業機制
- 一個會話查詢一條資料,這個資料就會被放在當前會話的一級快取中;
- 如果當前會話關閉了,這個會話對應的一級快取就沒了,但是我們想要的是,會話關閉了,一級快取中的資料被保存到二級快取中;
- 新的會話被查詢資訊,就可以從二級快取中獲取內容;
- 不同的mapper查出的資料會放在自己對應的快取(map)中;
小結:
- 只要開啟了二級快取,在同一個Mapper檔案下就有效;
- 所有的資料都會先放在一級快取中;
- 只有當會話提交,或者關閉的時候,才會提交到二級快取中,
總結:
- MyBatis一級快取的生命周期和SqlSession一致,
- MyBatis一級快取內部設計簡單,只是一個沒有容量限定的HashMap,在快取的功能性上有所欠缺,
- MyBatis的一級快取最大范圍是SqlSession內部,有多個SqlSession或者分布式的環境下,資料庫寫操作會引起臟資料,建議設定快取級別為Statement,
- MyBatis的二級快取相對于一級快取來說,實作了SqlSession之間快取資料的共享,同時粒度更加的細,能夠到namespace級別,通過Cache介面實作類不同的組合,對Cache的可控性也更強,
- MyBatis在多表查詢時,極大可能會出現臟資料,有設計上的缺陷,安全使用二級快取的條件比較苛刻,
- 在分布式環境下,由于默認的MyBatis Cache實作都是基于本地的,分布式環境下必然會出現讀取到臟資料,需要使用集中式快取將MyBatis的Cache介面實作,有一定的開發成本,直接使用Redis,Memcached等分布式快取可能成本更低,安全性也更高,
無論是一級快取還是二級快取,只要資料發生了寫操作(增、刪、改), 快取資料都將被自動清理
由于Mybatis的快取清理機制過于死板,所以,一般在開發實踐中并不怎么使用!更多的是使用其它的快取工具并自行制定快取策略
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/538913.html
標籤:Java
上一篇:day11_多型&抽象類&介面
