這樣理解 MyBatis 緩存機(jī)制,真香!
為了提高數(shù)據(jù)訪問(wèn)的性能,MyBatis 采用了一級(jí)緩存和二級(jí)緩存的緩存機(jī)制,那么它們是如何工作的?這篇文章,我們將詳細(xì)介紹這兩種緩存機(jī)制及其工作原理、配置方式和使用場(chǎng)景。
首先,看一張?jiān)韴D:
接著,我們來(lái)詳細(xì)地分析它們。
一、一級(jí)緩存
一級(jí)緩存(也叫本地緩存)是 MyBatis 默認(rèn)開(kāi)啟的,是基于 SqlSession 級(jí)別的緩存。也就是說(shuō),在同一個(gè) SqlSession 中,對(duì)于相同的查詢,如果參數(shù)相同,MyBatis 會(huì)從一級(jí)緩存中直接獲取數(shù)據(jù),而不會(huì)再去執(zhí)行數(shù)據(jù)庫(kù)查詢。
1. 特點(diǎn)
作用范圍:同一個(gè)SqlSession 實(shí)例。
默認(rèn)開(kāi)啟:無(wú)需額外配置。
清空時(shí)機(jī):
- 執(zhí)行insert、update、delete 操作時(shí),一級(jí)緩存會(huì)被清空。
- 調(diào)用SqlSession.clearCache() 方法手動(dòng)清空。
2. 工作原理
當(dāng)使用同一個(gè)SqlSession 執(zhí)行相同的 SQL 查詢時(shí),MyBatis 會(huì)先檢查一級(jí)緩存是否存在對(duì)應(yīng)的查詢結(jié)果。如果存在,則直接返回緩存中的結(jié)果;如果不存在,則執(zhí)行數(shù)據(jù)庫(kù)查詢并將結(jié)果放入一級(jí)緩存。
使用示例:
try (SqlSession session = sqlSessionFactory.openSession()) {
UserMapper mapper = session.getMapper(UserMapper.class);
User user1 = mapper.getUserById(1); // 查詢數(shù)據(jù)庫(kù),結(jié)果存入一級(jí)緩存
User user2 = mapper.getUserById(1); // 從一級(jí)緩存中獲取結(jié)果
// user1 和 user2 指向同一個(gè)對(duì)象
}
二、二級(jí)緩存(全局緩存)
二級(jí)緩存是基于 namespace(通常對(duì)應(yīng)于 Mapper 接口)的緩存,作用范圍超出了SqlSession,同一個(gè) mapper 的多個(gè)SqlSession 共享一級(jí)緩存之外的緩存。二級(jí)緩存需要顯式開(kāi)啟和配置。
1. 特點(diǎn)
作用范圍:同一個(gè) Mapper 的所有SqlSession 實(shí)例。
默認(rèn)關(guān)閉:需要在配置文件中手動(dòng)開(kāi)啟。
緩存實(shí)現(xiàn):需要配置緩存實(shí)現(xiàn)類,如 MyBatis 自帶的緩存或第三方緩存(例如 Ehcache、Redis)。
序列化:二級(jí)緩存中的對(duì)象需要序列化,以支持跨SqlSession 共享。
2. 啟用步驟
(1) 全局配置:在全局配置文件mybatis-config.xml 中開(kāi)啟二級(jí)緩存。
<configuration>
<settings>
<setting name="cacheEnabled" value="true"/>
</settings>
...
</configuration>
(2) Mapper 配置:在每個(gè)需要使用二級(jí)緩存的 Mapper XML 文件中配置<cache> 標(biāo)簽。
<mapper namespace="com.example.mapper.UserMapper">
<cache/>
<!-- SQL 語(yǔ)句定義 -->
</mapper>
或者使用自定義緩存實(shí)現(xiàn):
<cache type="org.mybatis.caches.ehcache.EhcacheCache"/>
(3) 映射接口注解:可以通過(guò)注解方式配置緩存(適用于 MyBatis 3.2 及以上版本)。
@CacheNamespace
public interface UserMapper {
// 方法定義
}
3. 緩存清理策略
MyBatis 二級(jí)緩存使用的是基于讀寫(xiě)的緩存策略,當(dāng)涉及數(shù)據(jù)修改操作(insert、update、delete)時(shí),會(huì)清理相關(guān) Mapper 的二級(jí)緩存,以保證數(shù)據(jù)一致性。
使用示例:
// 第一個(gè) SqlSession
try (SqlSession session1 = sqlSessionFactory.openSession()) {
UserMapper mapper1 = session1.getMapper(UserMapper.class);
User user1 = mapper1.getUserById(1); // 查詢數(shù)據(jù)庫(kù),結(jié)果存入一級(jí)和二級(jí)緩存
session1.commit();
}
// 第二個(gè) SqlSession
try (SqlSession session2 = sqlSessionFactory.openSession()) {
UserMapper mapper2 = session2.getMapper(UserMapper.class);
User user2 = mapper2.getUserById(1); // 從二級(jí)緩存中獲取結(jié)果
}
三、緩存的高級(jí)配置
1. 緩存刷新策略
可以通過(guò)<cache> 標(biāo)簽的屬性來(lái)配置緩存的刷新策略,如:
- eviction:指定緩存的清理策略(默認(rèn) LRU 策略)。
- flushInterval:指定緩存刷新時(shí)間間隔(單位:毫秒)。
- size:指定緩存的大小。
- readOnly:指定緩存是否為只讀。
示例:
<cache
eviction="FIFO"
flushInterval="60000"
size="512"
readOnly="true"/>
2. 使用第三方緩存
MyBatis 支持集成多種第三方緩存,如 Ehcache、Redis、Hazelcast 等。以 Ehcache 為例,配置步驟如下:
(1) 添加依賴:
<dependency>
<groupId>org.mybatis.caches</groupId>
<artifactId>mybatis-ehcache</artifactId>
<version>1.2.1</version>
</dependency>
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache</artifactId>
<version>2.10.6</version>
</dependency>
(2) 配置 Mapper:
<cache type="org.mybatis.caches.ehcache.EhcacheCache"/>
(3) 配置 Ehcache:
創(chuàng)建ehcache.xml 文件,配置緩存策略。
<ehcache>
<cache name="com.example.mapper.UserMapper"
maxEntriesLocalHeap="1000"
eternal="false"
timeToIdleSeconds="300"
timeToLiveSeconds="600"
overflowToDisk="false"/>
</ehcache>
四、緩存使用注意事項(xiàng)
數(shù)據(jù)一致性:使用二級(jí)緩存時(shí),需要確保緩存與數(shù)據(jù)庫(kù)的數(shù)據(jù)一致性,特別是在分布式環(huán)境下,推薦使用分布式緩存解決方案。
對(duì)象可序列化:二級(jí)緩存中的對(duì)象需要實(shí)現(xiàn)Serializable 接口,以支持緩存的序列化和反序列化。
避免緩存穿透:對(duì)高頻率的查詢,可以適當(dāng)配置緩存,從而減少數(shù)據(jù)庫(kù)的壓力。
緩存命中率:合理設(shè)計(jì)查詢語(yǔ)句和緩存策略,提升緩存的命中率,優(yōu)化性能。
五、總結(jié)
本文,我們?cè)敿?xì)地介紹了 MyBatis 的緩存策略,包括一級(jí)緩存適用于單次請(qǐng)求中的重復(fù)查詢,而二級(jí)緩存適用于跨請(qǐng)求的共享緩存。在實(shí)際應(yīng)用中,應(yīng)根據(jù)具體業(yè)務(wù)需求和系統(tǒng)架構(gòu),選擇合適的緩存策略和實(shí)現(xiàn)方式,以達(dá)到最佳的性能優(yōu)化效果。