介绍一下MyBatis缓存?

MyBatis 缓存机制是其性能优化的一大亮点,主要分为 一级缓存(Local Cache)二级缓存(Global Cache)。下面分别从原理、特性、使用方式等维度深入讲解:


一、一级缓存(Local Cache)

1. 概念

  • 一级缓存是 SqlSession 级别 的缓存,默认开启且无法关闭。

  • 同一个 SqlSession 中执行的 相同 SQL 查询,MyBatis 会先从缓存中取结果,而不是再次访问数据库。

2. 缓存作用域

  • 仅限当前 SqlSession,不同 SqlSession 不共享。

  • SqlSession 一关闭,缓存就会被清除。

3. 生效条件(缓存命中)

以下情况满足才会命中缓存:

  • 相同的 statementId + SQL + 参数

  • 同一个 SqlSession

  • 中间没有执行过更新操作(增删改)

4. 失效场景

  • 执行了 update / insert / delete 操作,会清空该 SqlSession 的缓存。

  • 手动调用 clearCache()

  • SqlSession 关闭。

✅ 使用示例:

SqlSession session = sqlSessionFactory.openSession();
User u1 = session.selectOne("getUserById", 1); // 查询数据库
User u2 = session.selectOne("getUserById", 1); // 命中一级缓存

二、二级缓存(Global Cache)

1. 概念

  • 二级缓存是 mapper namespace 级别 的缓存(可视作多个 SqlSession 共享的缓存)。

  • 默认 不启用,需要手动开启。

  • 二级缓存的作用范围是 同一个 Mapper 接口的所有 SqlSession 实例

2. 开启方式

a. 开启全局配置(mybatis-config.xml):
<setting name="cacheEnabled" value="true"/>
b. Mapper XML 中配置缓存:
<cache/>
c. 实体类必须实现 Serializable 接口。

3. 缓存触发 & 清理机制

  • 查询结果会被存入二级缓存(前提是当前 SqlSession 提交后),被其他 SqlSession 命中。

  • 若有任意更新操作(增删改),会清除当前 namespace 下的二级缓存。

✅ 示例:

// 第一次查询,缓存为空,从数据库加载
try (SqlSession session1 = sqlSessionFactory.openSession()) {
    User user1 = session1.selectOne("getUserById", 1);
    session1.commit(); // 必须 commit,缓存才写入二级缓存
}

// 第二次查询,从二级缓存读取(无需数据库)
try (SqlSession session2 = sqlSessionFactory.openSession()) {
    User user2 = session2.selectOne("getUserById", 1);
}

三、缓存结构与原理(核心类)

  • MyBatis 缓存结构本质是一个装饰器链:

    CachingExecutor -> BaseExecutor -> ...
    
  • 默认缓存实现类为:PerpetualCache(底层就是 HashMap)。

  • 可以通过 <cache type="xxx"> 指定自定义缓存实现。


四、自定义缓存实现(可选)

MyBatis 支持插件式缓存机制,可自定义缓存实现类:

<cache type="com.example.MyCustomCache"/>

只需实现接口:org.apache.ibatis.cache.Cache


五、一级 vs 二级缓存对比

特性一级缓存二级缓存
生命周期SqlSession 生命周期Mapper 生命周期
是否默认开启
是否可配置是(支持定制)
并发支持不需要(线程独占)可支持(需线程安全)
清除机制增删改操作 / 手动清空同 Mapper 增删改即清空

六、缓存使用建议

✅ 建议开启二级缓存的情况:

  • 读多写少的场景

  • 查询代价较高、变化不频繁的业务,如字典表、配置表

❌ 不建议开启:

  • 实时性要求高的系统

  • 数据频繁更新,缓存可能造成脏读


七、常见问题

  1. 为什么二级缓存没有生效?

    • 没有调用 SqlSession.commit()(缓存不会写入)

    • 查询对象未实现 Serializable

    • XML 中未配置 <cache/>

    • 有增删改清空了缓存

  2. 如何与 Redis 整合二级缓存?


八、总结一句话

一级缓存提升了SqlSession 级别的查询效率,二级缓存则在全局层面复用查询结果。合理开启缓存机制能显著减少数据库访问、提升性能,但需结合业务实际,避免脏读和缓存不一致问题。

如需深入:我可以继续讲讲如何结合 Redis、Spring 缓存机制实现分布式缓存,或者剖析 MyBatis 缓存的底层源码(如 Cache 接口、装饰器模式等)。是否继续?

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值