菜鸟-创作你的创作

Mybatis两级缓存可能导致的问题详细讲解

好的,我来帮你系统、详细地讲解 MyBatis 两级缓存(一级缓存 + 二级缓存)可能导致的问题,包括原因、场景、解决方案,并带示例说明。内容会尽量专业且实用。


MyBatis 两级缓存可能导致的问题详解

MyBatis 的缓存分为:

  1. 一级缓存(SqlSession 级别)
    • 默认开启,不可关闭
    • 生命周期:SqlSession 生命周期内
    • 数据隔离:每个 SqlSession 独立缓存
  2. 二级缓存(Mapper 级别,全局共享缓存)
    • 默认关闭,需要在 mapper.xml 或 Mapper 接口上开启 <cache/>
    • 生命周期:应用程序运行期间
    • 数据隔离:同一个 namespace(Mapper)共享缓存

MyBatis 缓存可以显著提高查询性能,但同时也可能引发数据不一致、脏数据、缓存污染等问题。


1. 一级缓存可能导致的问题

1.1 数据不一致

场景:同一个 SqlSession 中先查询再更新,再查询可能得到旧数据。

SqlSession session = sqlSessionFactory.openSession();
UserMapper mapper = session.getMapper(UserMapper.class);

// 查询用户信息
User user1 = mapper.getUserById(1); // 缓存 user1
// 修改数据库中该用户的名称(通过另一个 SqlSession 或直接 SQL 更新)
mapper.updateUserName(1, "新的名字");

// 再次查询
User user2 = mapper.getUserById(1);

问题user2 可能还是旧数据,因为一级缓存会直接返回 user1

解决方案

session.clearCache(); // 清空一级缓存


1.2 SqlSession 生命周期不当

如果你在 长生命周期的 SqlSession 中频繁查询:

建议


2. 二级缓存可能导致的问题

二级缓存是 跨 SqlSession、同一个 Mapper namespace 的共享缓存,所以问题更复杂。

2.1 脏数据(Stale Data)

场景:用户表 User 在多个 SqlSession 中操作:

  1. Session1 查询用户 1 → 数据缓存到二级缓存
  2. Session2 更新用户 1
  3. Session1 再次查询 → 读取二级缓存 → 得到旧数据

原因:二级缓存不会自动感知数据库变化,除非 MyBatis 执行了 增删改操作清除缓存(默认只清除本 Mapper 缓存)。

解决方案

&lt;cache 
    eviction="LRU" 
    flushInterval="60000"  &lt;!-- 60秒自动刷新 -->
    size="512" 
    readOnly="false"/>


2.2 数据一致性问题

解决方案

sqlSession.getConfiguration()
    .getCache(UserMapper.class.getName())
    .clear();


2.3 高并发下的缓存击穿 / 并发问题

解决方案


2.4 内存占用问题

解决方案

&lt;cache eviction="LRU" size="1000" flushInterval="300000"/>


2.5 缓存更新粒度过粗

解决方案


3. 总结与建议

缓存级别主要问题建议
一级缓存脏数据、生命周期过长SqlSession 尽量短生命周期;手动 clearCache()
二级缓存脏数据、并发问题、内存占用、缓存颗粒粗配置缓存刷新策略、限制大小、读写模式;高并发或复杂业务可考虑外部缓存

4. 小结

退出移动版