首頁 > 藝術

圖解|聊聊 MyBatis 快取

由 ITPUB 發表于 藝術2023-02-07

簡介3 一級快取考題考題(1)只開啟了一級快取,下面的程式碼呼叫了三次查詢操作 getStudentById,請判斷,下列說法正確的是

pagehelper是先查詢後分頁嗎

圖解|聊聊 MyBatis 快取

本文主要內容如下:

圖解|聊聊 MyBatis 快取

一、MyBatis 快取中的常用概念

MyBatis 快取:它用來最佳化 SQL 資料庫查詢的,但是可能會產生髒資料。

SqlSession:代表和資料庫的一次會話,向用戶提供了操作資料庫的方法。

MappedStatement:代表要發往資料庫執行的指令,可以理解為是 SQL 的抽象表示。

Executor:代表用來和資料庫互動的執行器,接受 MappedStatment 作為引數。

namespace:每個 Mapper 檔案只能配置一個 namespace,用來做 Mapper 檔案級別的快取共享。

對映介面:定義了一個介面,然後裡面的介面方法對應要執行 SQL 的操作,具體要執行的 SQL 語句是寫在對映檔案中。

對映檔案:MyBatis 編寫的 XML 檔案,裡面有一個或多個 SQL 語句,不同的語句用來對映不同的介面方法。通常來說,每一張單表都對應著一個對映檔案。

二、MyBatis 一級快取

2。1 一級快取原理

在一次 SqlSession 中(資料庫會話),程式執行多次查詢,且查詢條件完全相同,多次查詢之間程式沒有其他增刪改操作,則第二次及後面的查詢可以從快取中獲取資料,避免走資料庫。

圖解|聊聊 MyBatis 快取

每個SqlSession中持有了Executor,每個Executor中有一個LocalCache。當用戶發起查詢時,MyBatis根據當前執行的語句生成MappedStatement,在Local Cache進行查詢,如果快取命中的話,直接返回結果給使用者,如果快取沒有命中的話,查詢資料庫,結果寫入Local Cache,最後返回結果給使用者。

Local Cache 其實是一個 hashmap 的結構:

private Map cache = new HashMap();

如下圖所示,有兩個 SqlSession,分別為 SqlSession1 和 SqlSession2,每個 SqlSession 中都有自己的快取,快取是 hashmap 結構,存放的鍵值對。

鍵是 SQL 語句組成的 Key :

Statement Id + Offset + Limmit + Sql + Params

值是 SQL 查詢的結果:

圖解|聊聊 MyBatis 快取

2。2 一級快取配置

在 mybatis-config。xml 檔案配置,name=localCacheScope,value有兩種值:SESSION 和 STATEMENT

SESSION:開啟一級快取功能

STATEMENT:快取只對當前執行的這一個 SQL 語句有效,也就是沒有用到一級快取功能。

首先我們通過幾個考題來體驗下 MyBatis 一級快取。

2。3 一級快取考題

考題(1)只開啟了一級快取,下面的程式碼呼叫了三次查詢操作 getStudentById,請判斷,下列說法正確的是?

// 開啟一個 SqlSession

SqlSession sqlSession = factory。openSession(true);

StudentMapper studentMapper = sqlSession。getMapper(StudentMapper。class);

// 根據 id=1 查詢學生資訊

System。out。println(studentMapper。getStudentById(1));

// 根據 id=1 查詢學生資訊

System。out。println(studentMapper。getStudentById(1));

// 根據 id=1 查詢學生資訊

System。out。println(studentMapper。getStudentById(1));

答案:第一次從資料庫查詢到的資料,第二次和第二次從 MyBatis 一級快取查詢的資料。

解答:第一次從資料庫查詢後,後續查詢走 MyBatis 一級快取

考題(2)只開啟了一級快取,下面程式碼示例中,開啟了一個 SqlSession 會話,呼叫了一次查詢,然後對資料進行了更改,又呼叫了一次查詢,下列關於兩次查詢的說法,正確的是?

// 開啟一個 SqlSession

SqlSession sqlSession = factory。openSession(true);

StudentMapper studentMapper = sqlSession。getMapper(StudentMapper。class);

// 根據 id=1 查詢學生資訊

System。out。println(studentMapper。getStudentById(1));

// 插入了一條學生資料,改變了資料庫

System。out。println(“增加了” + studentMapper。addStudent(buildStudent()) + “個學生”);

// 根據 id=1 查詢學生資訊

System。out。println(studentMapper。getStudentById(1));

sqlSession。close();

答案:第一次從資料庫查詢到的資料,第二次從資料庫查詢的資料

解答:第一次從資料庫查詢後,後續更新(包括增刪改)資料庫中的資料後,這條 SQL 語句的快取失效了,後續查詢需要重新從資料庫獲取資料。

考題(3)當開啟了一級快取,下面的程式碼中,開啟了兩個 SqlSession,第一個 SqlSession 查詢了兩次學生 A 的姓名,第二次 SqlSession 更新了一次學生 A 的姓名,請判斷哪個選項符合最後的查詢結果。

SqlSession sqlSession1 = factory。openSession(true);

SqlSession sqlSession2 = factory。openSession(true);

StudentMapper studentMapper = sqlSession1。getMapper(StudentMapper。class);

StudentMapper studentMapper2 = sqlSession2。getMapper(StudentMapper。class); studentMapper2。updateStudentName(“B”,1);

System。out。println(studentMapper。getStudentById(1));

System。out。println(studentMapper2。getStudentById(1));

答案:

A

B

解答:只開啟一級快取的情況下,SqlSession 級別是不共享的。程式碼示例中,分別建立了兩個 SqlSession,在第一個 SqlSession 中查詢學生 A 的姓名,第二個 SqlSession 中修改了學生 A 的姓名為 B,SqlSession2 更新了資料後,不會影響 SqlSession1,所以 SqlSession1 查到的資料還是 A。

2。4 MyBatis 一級快取失效的場景

不同的SqlSession對應不同的一級快取

同一個SqlSession但是查詢條件不同

同一個SqlSession兩次查詢期間執行了任何一次增刪改操作

同一個SqlSession兩次查詢期間手動清空了快取

2。5 MyBatis 一級快取總結

MyBatis一級快取內部設計簡單,只是一個沒有容量限定的 HashMap,在快取的功能性上有所欠缺

MyBatis的一級快取最大範圍是SqlSession內部,有多個SqlSession或者分散式的環境下,資料庫寫操作會引起髒資料,建議設定快取級別為Statement

一級快取的配置中,預設是 SESSION 級別,即在一個MyBatis會話中執行的所有語句,都會共享這一個快取。

三、MyBatis 二級快取

3。1 MyBatis 二級快取概述

MyBatis的二級快取相對於一級快取來說,實現了SqlSession之間快取資料的共享,同時粒度更加的細,能夠到namespace級別,透過Cache介面實現類不同的組合,對Cache的可控性也更強。

MyBatis在多表查詢時,極大可能會出現髒資料,有設計上的缺陷,安全使用二級快取的條件比較苛刻。

在分散式環境下,由於預設的MyBatis Cache實現都是基於本地的,分散式環境下必然會出現讀取到髒資料,需要使用集中式快取將 MyBatis的Cache 介面實現,有一定的開發成本,直接使用Redis、Memcached 等分散式快取可能成本更低,安全性也更高。

3。2 MyBatis 二級快取原理

一級快取最大的共享範圍就是一個 SqlSession 內部,如果多個 SqlSession 之間需要共享快取,則需要使用到二級快取。

開啟二級快取後,會使用 CachingExecutor 裝飾 Executor,進入一級快取的查詢流程前,先在CachingExecutor 進行二級快取的查詢。

二級快取開啟後,同一個 namespace下的所有操作語句,都影響著同一個Cache。

圖解|聊聊 MyBatis 快取

每個 Mapper 檔案只能配置一個 namespace,用來做 Mapper 檔案級別的快取共享。

二級快取被同一個 namespace 下的多個 SqlSession 共享,是一個全域性的變數。MyBatis 的二級快取不適應用於對映檔案中存在多表查詢的情況。

通常我們會為每個單表建立單獨的對映檔案,由於MyBatis的二級快取是基於namespace的,多表查詢語句所在的namspace無法感應到其他namespace中的語句對多表查詢中涉及的表進行的修改,引發髒資料問題。

3。3 MyBatis快取查詢的順序

圖解|聊聊 MyBatis 快取

先查詢二級快取,因為二級快取中可能會有其他程式已經查出來的資料,可以拿來直接使用

如果二級快取沒有命中,再查詢一級快取

如果一級快取也沒有命中,則查詢資料庫

SqlSession關閉之後,一級快取中的資料會寫入二級快取。

3。4 二級快取配置

開啟二級快取需要在 mybatis-config。xml 中配置:

3。5 二級快取考題

測試update操作是否會重新整理該namespace下的二級快取。

開啟了一級和二級快取,透過三個SqlSession 查詢和更新 學生張三的姓名,判斷最後的輸出結果是什麼?

SqlSession sqlSession1 = factory。openSession(true);

SqlSession sqlSession2 = factory。openSession(true);

SqlSession sqlSession3 = factory。openSession(true);

StudentMapper studentMapper = sqlSession1。getMapper(StudentMapper。class);

StudentMapper studentMapper2 = sqlSession2。getMapper(StudentMapper。class);

StudentMapper studentMapper3 = sqlSession3。getMapper(StudentMapper。class); System。out。println(“studentMapper讀取資料: ” + studentMapper。getStudentById(1));

sqlSession1。commit();

System。out。println(“studentMapper2讀取資料: ” + studentMapper2。getStudentById(1)); studentMapper3。updateStudentName(“李四”,1);

sqlSession3。commit();

System。out。println(“studentMapper2讀取資料: ” + studentMapper2。getStudentById(1));

答案:

張三

張三

李四

解答:三個 SqlSession 是共享 MyBatis 快取,SqlSession2 更新資料後,MyBatis 的 namespace 快取(StudentMapper) 就失效了,SqlSession2 最後是從資料庫查詢到的資料。

四、MyBatis 自定義快取

4。1 MyBatis 自定義快取概述

當 MyBatis 二級快取不能滿足要求時,可以使用自定義快取替換。(較少使用)

自定義快取需要實現 MyBatis 規定的介面:org。apache。ibatis。cache。Cache。這個接口裡面定義了 7 個方法,我們需要自己去實現對應的快取邏輯。

圖解|聊聊 MyBatis 快取

4。2 整合第三方快取 EHCache

EHCache 和 MyBatis 已經幫我們整合好了一個自定義快取,我們可以直接拿來用,不需要自己去實現 MyBatis 的 org。apache。ibatis。cache。Cache 介面。

新增 mybatis-ehcache 依賴包。

org。mybatis。caches

mybatis-ehcache

1。2。1

建立EHCache的配置檔案ehcache。xml。

<?xml version=“1。0” encoding=“utf-8” ?>

xsi:noNamespaceSchemaLocation=“。。/config/ehcache。xsd”>

<!—— 磁碟儲存路徑 ——>

maxElementsInMemory=“1000”

maxElementsOnDisk=“10000000”

eternal=“false”

overflowToDisk=“true”

timeToIdleSeconds=“120”

timeToLiveSeconds=“120”

diskExpiryThreadIntervalSeconds=“120”

memoryStoreEvictionPolicy=“LRU”>

設定二級快取的型別,在xxxMapper。xml檔案中設定二級快取型別

4。3 EHCache配置檔案說明

圖解|聊聊 MyBatis 快取

五、總結

本篇分別介紹了 MyBatis 一級快取、二級快取、自定義快取的原理和使用,其中還穿插了 4 道考題來驗證 MyBatis 快取的功能。不足之處是 MyBatis 快取原始碼未分析。

參考資料:

https://tech。meituan。com/2018/01/19/mybatis-cache。html

Tags:快取MyBatisSqlSession查詢二級快取