MyBatis 中的缓存失效是什么时候发生的

2025-06-17 14:33:21 73

一、一级缓存(SqlSession级别缓存)

一级缓存默认开启,作用域为SqlSession。当同一个SqlSession执行相同的SQL查询时,MyBatis会优先从一级缓存中取结果。

失效时机:

  1. SqlSession执行增删改操作(insert、update、delete)

    • 任何增删改操作都会清空当前SqlSession的一级缓存,防止出现脏数据。

  2. SqlSession提交(commit)或回滚(rollback)

    • 执行commit()rollback()方法时会清空一级缓存。

  3. 手动清空缓存

    • 调用SqlSession.clearCache()方法会清空当前SqlSession的一级缓存。

  4. 执行SQL语句时设置了flushCache=true

    • 在映射文件中,如果某条语句配置了flushCache="true",执行该语句会清空一级缓存(和二级缓存)。
      示例:

      <select id="selectUser" resultType="User" flushCache="true">
          SELECT * FROM user WHERE id = #{id}
      </select>
  5. 查询不同的结果集

    • 当使用同一个方法查询不同参数时,会缓存不同的结果,但不会导致之前缓存失效。只有上述操作才会主动清除。

二、二级缓存(Mapper级别缓存)

二级缓存需要手动开启,作用域为Mapper(Namespace),多个SqlSession共享。

开启二级缓存:

  1. 在mybatis配置文件中开启全局缓存(默认开启,通常不需要设置):

    <settings>
        <setting name="cacheEnabled" value="true"/>
    </settings>
  2. 在Mapper映射文件中添加<cache/>标签。

失效时机:

  1. 执行增删改操作

    • 同一个Mapper中的增删改操作(insert、update、delete)执行后,会清空该Mapper的二级缓存。

    • 注意:配置了flushCache="true"的查询语句也会清空二级缓存(一般不这样配置查询)。

  2. 事务提交(commit)

    • 二级缓存只在SqlSession执行commit()close()时才会生效(将新数据刷入二级缓存),所以未提交的修改不会影响二级缓存。

    • 当多个SqlSession并发操作时,一个SqlSession提交才会更新二级缓存,此时其他SqlSession获取的是更新后的缓存。

  3. 手动清空缓存

    • 通过程序调用org.apache.ibatis.cache.Cacheclear()方法可以清空二级缓存(不常用)。

  4. 配置参数flushInterval

    • 如果配置了<cache flushInterval="60000"/>,则每60秒清空一次缓存。

  5. 配置参数size

    • 当缓存的对象数量超过设置的大小(默认为1024),会按照策略(如LRU)移除旧缓存。

  6. 其他注意事项

    • 二级缓存是跨SqlSession的,所以当多个SqlSession并发操作时,可能会读取到脏数据(需要合理使用事务隔离级别)。

    • 二级缓存默认是读写锁:同一时刻只有一个SqlSession能写入,写入时会阻塞所有读取操作,直到写入完成(可配置其他策略)。

三、如何避免缓存带来的问题?

  1. 一级缓存

    • 在分布式环境下,避免在同一个SqlSession中执行多个可能产生不一致的查询。

    • 对于实时性要求高的查询,可以手动清空缓存或设置flushCache=true

  2. 二级缓存

    • 适合读多写少的场景,避免频繁更新导致缓存频繁失效。

    • 实体类需要实现Serializable接口(因为二级缓存可能将对象序列化存储)。

    • 多个Mapper可以共享缓存,通过<cache-ref>标签引用同一缓存空间(一般不推荐,因为维护麻烦)。

四、代码示例

开启二级缓存

<!-- UserMapper.xml -->
<mapper namespace="com.example.UserMapper">
    <cache/> <!-- 开启二级缓存,使用默认配置 -->
</mapper>

配置二级缓存参数

<cache
  eviction="LRU"           <!-- 缓存回收策略:默认LRU -->
  flushInterval="60000"     <!-- 60秒刷新一次 -->
  size="1024"               <!-- 最大缓存对象数量 -->
  readOnly="true"          <!-- 只读,返回的实例不能被修改,可以提高性能 -->
/>

总结

MyBatis缓存的失效主要发生在:

  • 一级缓存:SqlSession执行增删改、提交、回滚、手动清空时。

  • 二级缓存:Mapper执行增删改、事务提交、手动清空、超时或达到上限时。

合理使用缓存可以提升性能,但需注意缓存带来的数据一致性问题。对于频繁修改的数据,建议关闭二级缓存,或根据业务场景设置合适的刷新策略。

在 MyBatis 中,缓存失效主要发生在一级缓存(本地缓存)和二级缓存(全局缓存)两个层面。以下是详细的失效场景:


一、一级缓存失效时机(SqlSession 级别)

  1. 执行写操作时自动失效

    sql复制INSERT, UPDATE, DELETE

    任何写操作(即使不修改当前缓存数据)都会清空整个一级缓存。

  2. 手动调用 clearCache()

    java复制sqlSession.clearCache();  // 立即清空当前 SqlSession 的缓存
  3. 事务提交/回滚时

    java复制sqlSession.commit();  // 或 rollback() // 提交/回滚后缓存自动清空(确保数据一致性)
  4. 执行语句时强制刷新
    在 Mapper XML 中设置 flushCache="true"

    xml复制<select id="selectUser" flushCache="true" resultType="User">
        SELECT * FROM user WHERE id = #{id}</select>
  5. SqlSession 关闭时

    java复制sqlSession.close();  // 所有缓存被销毁

二、二级缓存失效时机(Mapper 级别)

  1. 执行写操作后自动失效
    同命名空间(namespace)下的任何 INSERT/UPDATE/DELETE 操作会清空整个 namespace 的二级缓存。
    示例:执行 UserMapper.updateUser() 会清空所有 UserMapper 的缓存。

  2. 手动调用 clear()
    注入 Cache 对象强制清空:

    java复制Configuration configuration = sqlSession.getConfiguration();Cache cache = configuration.getCache("com.example.UserMapper");
    cache.clear();  // 清空 UserMapper 的二级缓存
  3. 配置缓存刷新策略
    通过 eviction 策略自动淘汰(如 LRU):

    xml复制<cache eviction="LRU" flushInterval="60000" size="512"/>
    • flushInterval:定期清空缓存(单位:毫秒)

    • size:缓存对象上限(超过时按策略淘汰)

  4. 事务提交时生效
    二级缓存的修改必须等到事务提交后才会更新(未提交的事务不会影响缓存)。


三、特殊注意点

  1. 跨 Mapper 缓存问题
    如果两个 Mapper 操作同一张表(如 UserMapper 和 OrderMapper 都操作 user 表),需要在 XML 中声明缓存引用:

    xml复制<cache-ref namespace="com.example.UserMapper"/>
  2. 缓存穿透风险
    频繁执行 flushCache="true" 或反复更新数据会导致缓存失效风暴,降低性能。

  3. 对象一致性要求
    二级缓存的对象必须是可序列化的:

    java复制public class User implements Serializable { ... }

四、最佳实践

  1. 读多写少场景:启用二级缓存提升性能。

  2. 写密集场景:禁用二级缓存(在配置中关闭):

    xml复制<cache enabled="false"/>
  3. 关键查询:对实时性要求高的查询显式禁用缓存:

    xml复制<select id="getRealTimeData" useCache="false">
        ...</select>
  4. 诊断工具:通过日志监控缓存命中率:

    xml复制<configuration>
      <settings>
        <setting name="cacheEnabled" value="true"/>
        <setting name="logImpl" value="STDOUT_LOGGING"/> 
      </settings></configuration>

总结图表

缓存级别失效触发条件影响范围
一级缓存UPDATE/INSERT/DELETE当前 SqlSession 全部

sqlSession.clearCache()

事务提交 (commit/rollback)

flushCache="true" 的查询
二级缓存同 namespace 的写操作整个 namespace 的缓存

调用 cache.clear()

到达 flushInterval 时间阈值

缓存对象数量超过 size 上限

合理使用缓存可提升性能,但需根据业务特点权衡一致性与效率。


相关文章

分类

{{name}}

标签

{{name}}

相关文章

广告区域
没有相关数据