MyBatis 一级缓存和二级缓存有什么区别?为什么会查到旧数据?
下面是一段教学用的模拟面试。
🧑💻 面试官:MyBatis 两级缓存有什么区别?
🙋♂️ 我:一级缓存属于 SqlSession,二级缓存可以被多个 SqlSession 使用。
🧑💻 面试官:那订单金额已经从 100 改成了 120,另一个查询为什么还返回 100?
🙋♂️ 我:可能是更新的时候没有清缓存。
🧑💻 面试官:如果更新来自另一个 JDBC 程序,MyBatis 会自动知道吗?两个 Mapper 查询同一张表,就一定共享二级缓存吗?
两级缓存不难记,难的是边界:「谁在共享这份结果,谁的更新能让它失效」。
面试速答(60 秒版)
MyBatis 的一级缓存通常作用在一个 SqlSession 内。同一会话里重复执行相同查询,可能直接使用本地结果,不再查询数据库。更新、提交、回滚、关闭会话等操作会清理本地缓存,也可以把范围设成单次语句执行。
二级缓存则按 namespace 组织,可以跨 SqlSession 使用,但需要实际启用和配置。查询结果在事务提交等边界后,才会按缓存管理规则对其他会话可见。
因此,两者都不是自动跟踪整张数据库表的缓存。别的程序修改数据,或者另一个没有共享缓存的 Mapper 更新同一张表,都可能让旧结果继续存在。
实际排查时,我会先检查 Session 生命周期、namespace、事务与失效配置,再检查数据库隔离级别,而不是直接认定“数据库没更新”。

知识点详解:缓存究竟保存在哪里、什么时候失效
一级缓存,是当前会话的查询记录
咱们假设一个 SqlSession 查询订单 42,拿到金额 100。后面这个 Session 又执行了相同查询,参数、语句等缓存条件也相同,那么它可能直接使用第一次留下的结果。
这里的“相同查询”不是只看 SQL 文本。语句标识、参数、分页信息等都会参与缓存键的组成。两个看起来很像的查询,不一定共享同一条结果。
一级缓存默认范围是 SESSION。执行更新、提交、回滚、关闭等操作会清理它,也可以调用 clearCache()。设置 localCacheScope=STATEMENT 后,本地缓存只在单次语句执行范围内使用,并不是完全移除了 MyBatis 内部对本地缓存的需要。官方 Java API 对这些边界有直接说明。
还有一个容易忽略的问题:Session 范围内返回的对象,可能就是缓存中保存的同一个对象。直接修改查询结果里的字段,后面再命中缓存,就可能看到自己改过的值。它不代表这次查询已经把数据库也修改了。
二级缓存,是 namespace 范围的共享结果
一级缓存不能直接借给其他 Session 使用。二级缓存解决的是跨会话复用,但它按 Mapper 的 namespace 组织,不是“同一张表自动共享”。
即使全局 cacheEnabled 为 true,也不等于所有 Mapper 已经建立二级缓存。还需要对应的缓存配置,例如 Mapper XML 的 <cache/>,以及查询是否允许使用缓存。
二级缓存带有事务管理。不能认为一个 Session 查询完,另一个 Session 下一行就必然能命中它;提交、回滚和关闭方式会影响待写入缓存的结果如何处理。
同一 namespace 下的增删改默认会触发相应缓存失效。如果多个 Mapper 需要共享同一个缓存,可以明确配置 cache-ref 等关系。但是把两个 Mapper 都指向同一张表,并不会自动建立这种关系。
数据库改了,为什么缓存仍然不知道?
继续看金额 100 的订单。
先通过 MyBatis 查询,结果被保存。随后,一个独立 JDBC 程序把金额改为 120。这个写入没有经过当前 MyBatis 的缓存管理,那么原缓存不会凭空收到“订单 42 已修改”的通知。
另一个常见情况是:查询放在 OrderQueryMapper,更新放在另一个没有共享缓存的 namespace。更新清理了自己的缓存,却没有清理前一个查询所在的缓存。
因此,高频变化、多个系统共同写入的数据,不适合只依赖默认二级缓存来保证新鲜度。可以不启用它,也可以设计明确的共享、失效和可接受陈旧范围;不能把失效问题交给一句“缓存能提速”。

接入 Spring 以后,还要看事务
在 MyBatis-Spring 中,SqlSession 由框架管理。事务内通常复用与该事务绑定的 Session;事务外的独立 Mapper 调用,不能凭“都在同一个 Java 方法里”就认定使用同一个长期 Session。
所以排查命中情况时,要把事务边界、调用方式和 Session 生命周期对上。连接池复用了一个数据库连接,也不能据此推导出 MyBatis 一级缓存必然复用。
面试官继续追问
关掉缓存以后还读到旧数据,是不是关错了?
不一定。数据库事务隔离和快照读也可能让同一事务看到旧版本。要确认缓存是否命中、SQL 是否真的执行,再区分应用缓存与数据库可见性。
二级缓存越多越好吗?
要看复用收益、数据变化速度和失效能否覆盖所有写入。更新频繁或存在外部写入的关键数据,正确性往往比省掉一次查询更重要。
连接池复用了同一个连接,一级缓存也会复用吗?
不能这样推导。连接和 SqlSession 是不同对象,要看实际会话与事务的生命周期,不是只看底层连接是不是同一个。
面试速记卡
- 一级缓存:默认 Session 内复用,本地缓存范围可配置为 STATEMENT。
- 二级缓存:namespace 范围共享,需要实际启用与配置。
- 事务边界:查询结束不等于结果立即对其他会话缓存可见。
- 失效边界:外部写入和未共享缓存的 Mapper,可能留下旧结果。
- 排查顺序:缓存命中、Session 与事务、namespace、数据库隔离逐项确认。
公司面试真题
真题根据求职者公开面经整理,题意经过概括,非逐字原话或公司官方题库;本文为 Sunday 的独立解析。
京东 · Java后台 · 校招
MyBatis 一级、二级缓存的原理和作用是什么?(题意整理)
京东 Java 后台三面凉经 ↗
原帖编辑于 2019-08-23(历史校招面经)