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

MyBatis 一級緩存與二級緩存原理深度解析

     編輯:Java 相關知識

MyBatis 雙級緩存架構全景深度剖析

MyBatis 提供兩級緩存機制應對高頻查詢性能瓶頸:一級緩存(SqlSession 級)默認開啟,二級緩存(Mapper 級)需顯式配置。本文從源碼層面完整追蹤查詢執行流程、緩存鍵生成策略、緩存實例化裝飾鏈,揭示核心設計原理。

一級緩存:SqlSession 級別的局部緩存

工作流程源碼追蹤

// DefaultSqlSession.selectList()
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
    MappedStatement ms = configuration.getMappedStatement(statement);
    return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
}

// BaseExecutor.query() - 構建 CacheKey
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) {
    BoundSql boundSql = ms.getBoundSql(parameter);
    CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
    return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
}

// CachingExecutor.query() - 二級緩存攔截點(若開啟)
// BaseExecutor.query() - 一級緩存核心邏輯
public <E> List<E> query(...) {
    if (queryStack == 0 && ms.isFlushCacheRequired()) clearLocalCache();
    List<E> list = (resultHandler == null) ? (List<E>) localCache.getObject(key) : null;
    if (list != null) handleLocallyCachedOutputParameters(...);
    else list = queryFromDatabase(...);
    return list;
}

三大核心結論

  1. 存儲位置:localCache 定義在 Executor(默認 SimpleExecutor)中,而非 SqlSession;SqlSession 關閉時 Executor 隨之回收
  2. 無條件存在:無論是否配置,只要 SqlSession 存在即擁有 PerpetualCache(底層 HashMap,無容量控制)
  3. 增刪改自動清空:BaseExecutor.update() 首行調用 clearLocalCache(),防止髒讀;flushCache="true" 可強制查詢前清空

CacheKey:三維度判定「同一查詢」

CacheKey 封裝 updateList,equals() 逐項比對:

  1. MappedStatement ID:namespace + select id
  2. RowBounds:offset、limit(分頁參數)
  3. SQL 語句:boundSql.getSql()(含參數佔位符)

三者完全一致且未配置 flushCache="true",直接命中一級緩存。

二級緩存:Mapper 級別的全局共享緩存

讀取優先級:二級 > 一級

CachingExecutor.query() 先檢查二級緩存(tcm.getObject()),未命中再委託 delegate(BaseExecutor)查詢一級緩存/數據庫。結論:開啟二級緩存時,二級緩存讀取優先級高於一級緩存。

核心邏輯

Cache cache = ms.getCache();  // 從 MappedStatement 獲取 Cache 實例
if (cache != null) {
    flushCacheIfRequired(ms);  // 根據 flushCache 屬性決定是否清理
    if (ms.isUseCache() && resultHandler == null) {
        List<E> list = (List<E>) tcm.getObject(cache, key);  // 事務緩存獲取
        if (list == null) {
            list = delegate.query(...);  // 回退一級緩存/DB
            tcm.putObject(cache, key, list);  // 寫入二級緩存
        }
        return list;
    }
}
return delegate.query(...);
  • tcm = TransactionalCacheManager,裝飾器模式管理事務緩存
  • 緩存鍵 CacheKey 與一級緩存共用,判定條件一致
  • 生命週期:MappedStatement 存儲在 Configuration.mappedStatements,應用啟動時構建,應用結束才銷毀

防 OOM 配置參數

  • size:最大條目數
  • eviction:淘汰算法(默認 LRU)
  • flushInterval:定期清理間隔
  • readOnly:只讀模式避免序列化開銷

二級緩存實例化:建造者 + 裝飾器模式鏈

XmlMapperBuilder.cacheElement() 解析配置

type="PERPETUAL" (默認) → PerpetualCache
eviction="LRU" (默認) → LruCache
flushInterval, size, readWrite, blocking, properties

CacheBuilder.build() 裝飾鏈構建

  1. 基礎緩存:newBaseCacheInstance(implementation, id) → PerpetualCache
  2. 淘汰裝飾:LruCache 包裝實現 LRU 淘汰
  3. 標準裝飾鏈(按序):
ScheduledCache (flushInterval) → 定時清理
SerializedCache (readWrite=true) → 序列化支援(要求值實現 Serializable)
LoggingCache → 命中率日誌
SynchronizedCache → 線程安全同步
BlockingCache (blocking=true) → 同鍵阻塞等待

三類緩存實現支援

  1. 默認:PerpetualCache + 全套裝飾
  2. 自定義:實現 Cache 接口,僅裝飾 LoggingCache
  3. 第三方:Ehcache、Redis 等,僅裝飾 LoggingCache(自帶淘汰/過期機制)

總結

MyBatis 緩存設計精妙體現於:一級緩存輕量無感、二級緩存可配置擴展、裝飾器模式靈活組合功能、CacheKey 統一語義。生產環境建議:默認開啟一級緩存、熱點數據開啟二級緩存並配置淘汰策略、分佈式場景接入 Redis 等中心化緩存替代本地二級緩存。

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