MyBatis一级缓存与二级缓存

ps:如果这篇帖子对于还在找工作和找实习的你有所帮助,可以关注我,给本贴点赞、评论、收藏并订阅专栏;同时不要吝啬您的花花

MyBatis作为主流的ORM框架,内置两级缓存机制,核心目的是减少数据库重复查询、提升接口响应效率。两级缓存层级分明、作用域不同,查询时遵循一级缓存 → 二级缓存 → 数据库的优先级顺序,合理使用能大幅优化系统性能,误用则可能引发数据不一致问题。下文将深度拆解两级缓存的原理、配置、生效规则与避坑要点,并用通俗语言讲清缓存生效场景。

一、一级缓存(本地缓存/会话缓存)

1.1 核心定义

一级缓存是MyBatis默认开启、无需额外配置的缓存,属于SqlSession级别的本地缓存。每个SqlSession会话拥有独立的缓存空间,仅当前会话可见,不同SqlSession之间的缓存完全隔离、互不共享,相当于会话的“私有数据仓库”。

通俗理解:SqlSession可以简单理解为一次数据库会话/一次连接,只要是同一会话内执行完全相同的SQL重复查询,就会直接走一级缓存,不用再次访问数据库,这也是一级缓存最核心的作用。

SpringBoot整合MyBatis场景:判断是否为会话级的核心方法

SpringBoot整合MyBatis时,SqlSession由Spring容器统一管理,判定是否为同一会话级,抓住2个核心标尺即可:1. 同请求/同事务=同一会话:Spring默认将SqlSession与单次HTTP请求、@Transactional事务绑定,同一个接口请求、同一个事务内,属于同一个SqlSession,重复SQL走一级缓存;2. 跨请求/新事务=新会话:不同接口请求、新开事务、事务提交/回滚/关闭后,都会创建全新SqlSession,不属于同一会话,一级缓存直接失效。极简口诀:一次请求/一个事务,就是一个会话;请求结束/事务结束,会话随之销毁。

高频实操问答(必看)

1. 一个接口内执行两次相同SQL,会走一级缓存吗?会命中,原因:单次接口请求对应同一个SqlSession,两次查询属于同一会话,且SQL、参数完全一致,满足一级缓存命中规则,第二次查询直接读取缓存,不查数据库。

2. 连续调用两次相同接口,会走一级缓存吗?不会命中,原因:每次调用接口都是独立的HTTP请求,Spring会创建全新的SqlSession,两次查询分属不同会话,一级缓存会话隔离,第二次查询必须重新访问数据库。

1.2 底层工作原理

MyBatis的SqlSession内部持有Executor执行器,每个Executor都绑定了一个LocalCache本地缓存对象。当执行查询操作时,框架会先根据SQL语句、参数、分页信息、绑定的Statement生成唯一缓存Key,先查询LocalCache是否存在对应数据:

  • 缓存命中:直接返回缓存数据,跳过数据库查询,减少IO开销
  • 缓存未命中:执行数据库查询,将结果写入LocalCache,再返回数据

一级缓存底层默认使用PerpetualCache(永久缓存)实现,存储介质为内存,读写效率极高。

1.3 生命周期与生效规则

一级缓存的生命周期完全绑定SqlSession,仅在以下场景生效:

  • 同一个SqlSession会话内执行完全相同的查询(SQL、参数、分页、Statement均一致)
  • 会话未执行commit/rollback、未关闭,缓存数据持续有效
  • 未执行任何会清空缓存的操作,缓存始终保留

当SqlSession调用close()关闭、commit提交事务,或会话销毁时,一级缓存会被彻底清空,数据不复存在。

1.4 缓存失效场景(核心避坑)

一级缓存并非永久有效,执行以下操作会强制清空当前会话的全部缓存,避免脏数据:

  • 执行增删改操作(insert/update/delete),无论是否提交事务,都会清空缓存
  • 手动调用SqlSession.clearCache()方法,主动清空缓存
  • 会话提交/回滚事务、关闭会话
  • 查询条件、SQL语句、参数发生变化,缓存Key不匹配,无法命中

一级缓存是会话级轻量缓存,无需手动管理,适合解决同一会话内重复查询的场景,无需担心跨会话数据不一致问题。

二、二级缓存(全局缓存/Mapper缓存)

2.1 核心定义

二级缓存是Mapper(namespace)级别的全局缓存,属于跨SqlSession共享的缓存机制,作用域覆盖同一Mapper命名空间下的所有SqlSession。它是应用级缓存,生命周期与SqlSessionFactory绑定,默认关闭,需手动配置启用。

2.2 开启配置(两步必做)

二级缓存需全局开关+Mapper开关配合启用,缺一不可:

步骤1:全局配置文件开启缓存总开关

<!-- mybatis-config.xml 全局配置 -->
<settings>
    <!-- 开启二级缓存,默认值为true,建议显式配置 -->
    <setting name="cacheEnabled" value="true"/>
</settings>

步骤2:Mapper XML文件启用当前命名空间缓存

<!-- UserMapper.xml 对应namespace的缓存配置 -->
<mapper namespace="com.example.mapper.UserMapper">
    <!-- 启用二级缓存,默认配置即可,也可自定义参数 -->
    <cache/>
</mapper>

二级缓存存储的是序列化后的对象,因此查询返回的实体类必须实现Serializable序列化接口,否则会抛出序列化异常。

2.3 底层工作原理

二级缓存基于CachingExecutor装饰器实现,在一级缓存的基础上增加全局缓存逻辑:

  1. 查询请求先进入一级缓存,未命中则查询二级缓存
  2. 二级缓存命中:反序列化数据返回,同时写入当前会话一级缓存
  3. 二级缓存未命中:查询数据库,结果写入一级缓存;SqlSession提交/关闭后,数据才会同步到二级缓存(避免未提交事务污染全局缓存)
  4. 同一Mapper下的其他SqlSession,可直接读取二级缓存中的共享数据

2.4 缓存同步与失效规则

二级缓存的清空粒度为Mapper命名空间,避免全局缓存污染:

  • 同一Mapper内执行增删改操作,提交事务后,会清空当前namespace的全部二级缓存
  • 不同Mapper的缓存相互独立,修改操作不会影响其他命名空间的缓存
  • 应用重启、SqlSessionFactory重建,二级缓存会彻底清空

2.5 核心特性与适用场景

  • 存储介质:默认内存,可扩展为Redis、Ehcache等分布式缓存
  • 共享范围:同一应用内,同一Mapper的所有SqlSession共享
  • 适用场景:查询频率高、修改频率极低的数据(如字典表、配置表、静态数据)
  • 禁用场景:频繁修改的业务数据、多线程并发修改场景(易引发脏读)

三、两级缓存通俗生效范围

核心结论:先看会话、再看Mapper,缓存生效范围一目了然

3.1 一级缓存在哪里生效?

仅限单次会话内,重复查同一条SQL

比如:打开一次数据库连接(一个SqlSession),连续执行2次select * from user where id=1,第一次查库、第二次直接走一级缓存;如果关闭会话重新连接(新SqlSession),再查这条SQL,一级缓存就失效了,必须重新查库。

3.2 二级缓存在哪里生效?

跨会话、跨请求,同一个Mapper内重复查同一条SQL

比如:用户A请求接口(创建SqlSessionA)查完user数据,提交会话后缓存同步到二级;用户B再请求同一个接口(创建SqlSessionB),查同一条user SQL,不用查库,直接走二级缓存;只要是同一个UserMapper里的查询,都能共享这份缓存,不同Mapper的缓存互不干扰。

四、一级缓存与二级缓存核心对比

作用级别

SqlSession(会话级)

Mapper Namespace(全局级)

默认状态

默认开启,无需配置

默认关闭,需手动配置

共享范围

仅当前SqlSession可见

跨SqlSession,同Mapper共享

生命周期

随SqlSession创建/销毁

随SqlSessionFactory/应用存活

存储形式

内存对象,无需序列化

序列化对象,需实现Serializable

清空粒度

当前会话全部缓存

当前Mapper命名空间缓存

风险等级

无数据不一致风险

误用易引发脏读、数据不一致

五、实战使用建议

  • 一级缓存:无需手动关闭,默认使用即可,解决会话内重复查询问题,无额外维护成本
  • 二级缓存:谨慎启用,仅针对只读、少改的数据开启;高频修改的业务表坚决禁用
  • 分布式环境:默认二级缓存不适用,需整合Redis、Ehcache等分布式缓存实现
  • 单条查询禁用:可在Statement标签添加useCache="false",关闭当前查询的二级缓存

六、总结

MyBatis一级缓存是轻量、安全的会话级缓存,同会话重复SQL直接命中,是框架默认的性能优化手段;二级缓存是全局共享缓存,跨会话、同Mapper可共享缓存,性能提升更显著,但需严格管控使用场景。核心原则是:一级缓存放心用,二级缓存谨慎用,结合业务数据的读写特性选择缓存策略,才能兼顾性能与数据一致性

ps:如果这篇帖子对于还在找工作和找实习的你有所帮助,可以关注我,给本贴点赞、评论、收藏并订阅专栏;同时不要吝啬您的花花

MyBatis 文章被收录于专栏

本专栏聚焦Java主流持久层框架MyBatis,从基础搭建到源码原理,系统拆解核心组件、动态SQL、结果映射与缓存机制。助力开发者从入门到精通,掌握高效数据层开发技能,适配电商、金融等复杂业务场景。

全部评论

相关推荐

03-19 10:36
云南大学 C++
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务