MyBatis 一级缓存与二级缓存

候选人小段在面试字节 P6 时,面试官问道:

"MyBatis 的缓存是怎么工作的?"

小段说:"有一级缓存和二级缓存..."

面试官追问:"一级缓存和二级缓存有什么区别?"

小段说:"一级缓存是 SqlSession 级别的..."

面试官继续追问:"那二级缓存什么时候失效?"

小段答不上来。

【面试官心理】 这道题我用来测试候选人对 MyBatis 缓存机制的理解。缓存是 MyBatis 的核心特性,能说清一级缓存的会话级别、二级缓存的 namespace 级别、以及缓存失效条件的,是真正理解 MyBatis 的候选人。


一、核心问题 🔴

1.1 问题拆解

第一层:缓存层级

  • "MyBatis 有一级缓存和二级缓存,分别是什么?"
  • "一级缓存和二级缓存的区别是什么?"

第二层:原理

  • "一级缓存是怎么实现的?"
  • "二级缓存是怎么实现的?"

第三层:细节

  • "什么情况下缓存会失效?"
  • "二级缓存的 flushInterval 是做什么的?"

1.2 标准回答

P5 回答:区别对比

对比维度一级缓存(本地缓存)二级缓存(全局缓存)
作用范围SqlSession(会话级别)Mapper(namespace 级别)
存储位置PerpetualCache(HashMap)同上
生命周期SqlSession 关闭后清除应用级,永久有效
默认开启
存储内容查询结果查询结果 + 变更通知

1.3 追问升级

追问 1:一级缓存的实现

// BaseExecutor 中的一级缓存
public abstract class BaseExecutor implements Executor {
    // 一级缓存:PerpetualCache,底层是 HashMap
    private final PerpetualCache cache = new PerpetualCache("LocalCache");

    @Override
    @SuppressWarnings("unchecked")
    public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds,
            ResultHandler resultHandler, CacheKey key, BoundSql boundSql) {
        // 1. 查一级缓存
        list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;

        if (list != null) {
            // 命中缓存
            return list;
        }

        // 2. 缓存未命中,查询数据库
        list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);

        return list;
    }
}

追问 2:二级缓存的配置

<!-- Mapper XML 中开启二级缓存 -->
<cache
    eviction="LRU"
    flushInterval="60000"
    size="512"
    readOnly="true"/>

<!-- 或者使用 @CacheNamespace 注解 -->
@CacheNamespace(eviction = LruCache.class, flushInterval = 60000, size = 512)
public interface UserMapper { }

二、面试总结

MyBatis 缓存的核心是:一级缓存是 SqlSession 私有的,二级缓存是 Mapper namespace 级别共享的。