首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 綜合教學

MyBatis 緩存機制深度解析:一級緩存、二級緩存、Ehcache 整合與分布式擴展

     編輯:Java 綜合教學

MyBatis 緩存機制深度解析:一級緩存、二級緩存、Ehcache 整合與分布式擴展

緩存是 ORM 框架提升查詢性能的關鍵。MyBatis 提供一級緩存(SqlSession 級)與二級緩存(Mapper 級),並支持可插拔第三方緩存(Ehcache、Redis、Caffeine)。本文系統講解兩級緩存原理、配置、失效策略、整合 Ehcache 實現分布式緩存,並分析二級緩存局限性與最佳實踐。

一、緩存架構總覽

MyBatis緩存架構圖

  • 一級緩存:SqlSession 維度,默認開啟,生命週期與 SqlSession 綁定。不同 SqlSession 緩存隔離。
  • 二級緩存:Mapper(namespace)維度,默認關閉,需顯式開啟。同一 Mapper 下多個 SqlSession 共享緩存,跨會話生效。

二、一級緩存:會話級數據一致性保障

工作原理

一級緩存工作流程

  1. 查詢前先檢查緩存(Key:hashCode + sqlId + SQL語句)
  2. 未命中 → 執行 SQL → 結果存入緩存(HashMap)
  3. 增刪改提交(commit) → 清空當前 SqlSession 一級緩存(防止髒讀)
  4. 同一 SqlSession 再次查詢 → 直接命中緩存

Spring 整合場景下的實際效果

@Service
@Transactional
public class UserService {
    @Autowired UserMapper userMapper;

    public User getUser(Long id) {
        // 同一事務 = 同一 SqlSession
        User u1 = userMapper.selectById(id); // 查數據庫,寫入一級緩存
        User u2 = userMapper.selectById(id); // 命中一級緩存
        return u2; // u1 == u2 為 true(同一對象引用)
    }
}

事務結束 → SqlSession 關閉 → 一級緩存銷毀。跨事務查詢不共享一級緩存。

三、二級緩存:跨會話的共享緩存

啟用步驟

  1. 全局開關(SqlMapConfig.xml):
    <setting name="cacheEnabled" value="true"/>
  2. Mapper 級開啟(UserMapper.xml):
    <cache/> <!-- 使用默認 PerpetualCache,HashMap 實現 -->
  3. POJO 實現 Serializable:二級緩存可能序列化到磁盤/網絡
    public class User implements Serializable { ... }

工作原理

二級緩存工作流程

  1. SqlSession1 查詢 → 數據寫入內存(一級緩存)
  2. SqlSession1.close() → 一級緩存數據刷新至二級緩存區域(按 namespace 劃分)
  3. SqlSession2 查詢 → 直接命中二級緩存(反序列化得到副本,u1 != u2)
  4. 任意 SqlSession 執行增刷改 commit() → 清空該 Mapper namespace 下整個二級緩存區域

關鍵配置屬性

屬性 說明 默認值
eviction 回收策略:LRU/FIFO/SOFT/WEAK LRU
flushInterval 定時刷新間隔(毫秒),0=不定時刷新 0
size 最大引用對象數 1024
readOnly true=返回同一實例(高性能,不可修改);false=返回副本(安全) false
type 自定義緩存實現類(如 EhcacheCache) PerpetualCache

Statement 級控制

<select id="selectLatest" resultType="User" useCache="false"> 
    SELECT * FROM user ORDER BY create_time DESC LIMIT 1
</select>

<insert id="insert" flushCache="true"> ... </insert> <!-- 默認 true,提交後刷新緩存 -->

四、MyBatis 整合 Ehcache:邁向分布式緩存

為什麼需要 Ehcache

  • 默認二級緩存基於進程內 HashMap,集群環境下各節點緩存不一致
  • Ehcache 提供磁盤持久化、分布式複製(RMI/JGroups/Terracotta)
  • 成熟、純 Java、Hibernate 默認緩存提供者

整合步驟

  1. 引入依賴:mybatis-ehcache、ehcache、slf4j
  2. 類路徑下放置 ehcache.xml:
    <ehcache>
        <diskStore path="D:/ehcache"/>
        <defaultCache
            maxElementsInMemory="1000"
            maxElementsOnDisk="10000000"
            eternal="false"
            overflowToDisk="false"
            timeToIdleSeconds="120"
            timeToLiveSeconds="120"
            diskExpiryThreadIntervalSeconds="120"
            memoryStoreEvictionPolicy="LRU"/>
    </ehcache>
  3. Mapper 指定實現類:
    <cache type="org.mybatis.caches.ehcache.EhcacheCache">
        <property name="timeToIdleSeconds" value="3600"/>
        <property name="timeToLiveSeconds" value="3600"/>
        <property name="maxEntriesLocalHeap" value="1000"/>
        <property name="maxEntriesLocalDisk" value="10000000"/>
        <property name="memoryStoreEvictionPolicy" value="LRU"/>
    </cache>

應用場景

  • 高頻查詢、實時性要求不高:統計報表、賬單查詢、字典數據
  • 配置 flushInterval 定時自動失效(如 30 分鐘、1 小時)

五、二級緩存局限性與解決方案

  • 業務層手動清理相關緩存
  • 引入 Redis 實現細粒度 Key 失效
  • 業務層使用 Redis/本地緩存(Caffeine)針對性緩存
  • 避免在高寫入場景使用二級緩存
  • 整合 Ehcache 分布式模式 / Redis / Hazelcast
  • 局限 原因 解決方案
    多表關聯更新導致髒讀 緩存按 namespace 劃分,表 A 更新無法精確失效表 B 相關緩存
    細粒度緩存失效困難 Mapper 級緩存粒度粗,單條數據變更導致整個 Mapper 緩存清空
    分布式一致性 默認實現無分布式能力

    六、最佳實踐總結

    1. 一級緩存始終開啟,無需配置,善用事務邊界控制生命週期
    2. 二級緩存謹慎開啟:只讀、低頻變更、高頻查詢的參考數據(字典、配置、報表)
    3. 實體類實現 Serializable,正確配置 readOnly=true(不可修改場景)提升性能
    4. 增刷改語句保持 flushCache=true,防止髒讀
    5. 實時性要求高的查詢設置 useCache=false
    6. 集群環境必須整合分布式緩存(Ehcache Terracotta / Redis / Nginx 本地緩存 + 廣播失效)
    7. 業務層緩存策略 > 框架層緩存:關鍵業務數據在 Service 層用 Redis/Caffeine 精確控制

    MyBatis 緩存機制設計精妙,但二級緩存非銀彈。理解其原理、邊界與局限,結合業務場景分層緩存,才能構建高性能、一致性可控的數據訪問層。

    1. 上一篇文章:
    2. 下一篇文章:
    Copyright © 程式碼之家 All Rights Reserved