MyBatis 有哪些缓存? 一级缓存和二级缓存有什么区别?

MyBatis 提供了两种主要的内置缓存机制来提高查询性能,减少对数据库的直接访问:一级缓存 (Level 1 Cache)二级缓存 (Level 2 Cache)

1. 一级缓存 (Local Cache / SqlSession Cache)

  • 定义: 一级缓存是 SqlSession 级别的缓存。它在 MyBatis 中是默认开启且无法关闭的(但可以通过设置 localCacheScope=STATEMENT 使其作用范围仅限于语句执行期间,效果接近关闭,但通常不推荐)。
  • 作用域: 它的生命周期与 SqlSession 相同。当 SqlSession 被创建时,一个新的一级缓存实例(本质上是一个 HashMap) 被创建。当 SqlSession 关闭 (close()) 时,一级缓存被销毁。
  • 工作原理: 当同一个 SqlSession 执行完全相同的查询(相同的 MappedStatement ID、相同的参数、相同的分页信息等)时,第一次查询会从数据库获取数据并将其放入一级缓存中。后续的相同查询会直接从一级缓存中获取结果,而不会再次访问数据库。
  • 缓存数据: 缓存的是查询结果的对象引用本身。
  • 失效场景:
    • SqlSession 关闭时。
    • 当执行了 commit() 操作(即使没有实际修改数据),MyBatis 会认为数据可能已变更。
    • 当执行了任何增删改 (INSERT, UPDATE, DELETE) 操作时,无论这些操作是否影响到缓存中的数据,MyBatis 都会清空当前 SqlSession 的一级缓存,以防止读到脏数据。
    • 手动调用 sqlSession.clearCache() 方法。
  • 特点:
    • SqlSession 隔离: 每个 SqlSession 拥有自己独立的一级缓存,不同 SqlSession 之间的一级缓存互不影响。
    • 默认开启: 无需额外配置。
    • 生命周期短:SqlSession 绑定。

2. 二级缓存 (Global Cache / Namespace Cache)

  • 定义: 二级缓存是 Mapper Namespace 级别(或称为 SqlSessionFactory 级别)的缓存。它可以被多个 SqlSession 共享
  • 作用域: 它的生命周期与应用程序(或者说 SqlSessionFactory)相同,只要应用程序运行,二级缓存就可能存在(除非被配置的策略清除)。
  • 工作原理: 当一个 SqlSession 执行查询并提交 (commit()) 后,如果该查询配置了使用二级缓存,查询结果会被存入对应的 Mapper Namespace 的二级缓存中。当其他 SqlSession 执行相同的查询时,会先尝试从该 Namespace 的二级缓存中获取数据。
  • 缓存数据: 为了能在多个 SqlSession 之间安全共享,并且支持序列化到磁盘或分布式缓存,二级缓存通常存储的是查询结果对象的序列化副本(除非配置为 readOnly=true,这时存储的是对象引用,存在线程安全风险)。因此,放入二级缓存的对象必须实现 Serializable 接口
  • 开启配置: 二级缓存默认是关闭的。需要手动配置才能开启:
    1. mybatis-config.xml 全局配置文件中开启二级缓存总开关:
      <settings>
          <setting name="cacheEnabled" value="true"/>
      </settings>
      
    2. 在需要开启二级缓存的 Mapper XML 文件中使用 <cache/> 标签:
      <mapper namespace="com.example.mapper.UserMapper">
          <!-- 开启二级缓存,可配置 eviction, flushInterval, size, readOnly 等属性 -->
          <cache eviction="FIFO" flushInterval="60000" size="512" readOnly="false"/>
      
          <select id="selectUserById" resultType="User" useCache="true"> <!-- useCache 默认为 true -->
              SELECT * FROM users WHERE id = #{id}
          </select>
      
          <update id="updateUser" flushCache="true"> <!-- flushCache 默认为 true -->
             UPDATE users SET name = #{name} WHERE id = #{id}
          </update>
      </mapper>
      
    3. 确保相关实体类实现了 java.io.Serializable 接口。
  • 失效场景:
    • 当同一个 Mapper Namespace 下执行了任何增删改 (INSERT, UPDATE, DELETE) 操作(并且对应的语句没有设置 flushCache="false")时,该 Namespace 的二级缓存会被清空(或根据配置可能只清除部分)。这是为了保证数据的一致性。
    • 可以通过 <cache/> 标签的 flushInterval 属性设置定时刷新。
    • 可以通过 <cache/> 标签的 eviction 属性设置缓存淘汰策略(如 LRU, FIFO)。
    • 可以手动通过代码获取 Cache 对象并调用 clear() 方法。
  • 特点:
    • 跨 SqlSession 共享: 多个 SqlSession 可以共享同一个 Namespace 的缓存数据。
    • 默认关闭: 需要显式配置开启。
    • 生命周期长: 与应用程序生命周期相关。
    • 配置灵活: 可以配置淘汰策略、刷新间隔、缓存大小、只读/读写模式、集成第三方缓存(如 Ehcache, Redis)等。
    • 数据一致性风险: 由于是共享缓存,如果数据被外部系统修改,或者在分布式环境下节点间缓存同步不及时,可能导致读到脏数据。readOnly="false"(默认)通过返回序列化副本降低了对象被意外修改的风险,但有性能开销;readOnly="true" 性能更好但有线程安全和数据修改风险。

一级缓存和二级缓存的主要区别总结:

特性一级缓存 (Level 1 Cache)二级缓存 (Level 2 Cache)
作用域SqlSession 级别Mapper Namespace 级别 (跨 SqlSession)
生命周期SqlSession 同步,Session 关闭即销毁与应用程序生命周期相关,直到被清除或应用停止
共享性不共享,每个 SqlSession 独立共享,同一 Namespace 下的多个 SqlSession 可共享
默认状态默认开启默认关闭
存储内容对象引用对象序列化副本 (通常,除非 readOnly=true)
配置无需配置 (基本无法关闭)需要全局 <setting> 和 Mapper <cache/> 标签配置,实体需序列化
主要目的减少同一事务/会话内的重复数据库查询减少不同事务/会话间对相同数据的重复数据库查询
数据一致性相对容易保证(事务内)需谨慎处理,尤其在并发或分布式场景下可能存在脏读风险

缓存查询顺序:

当执行一个查询时,MyBatis 会按照以下顺序查找缓存:

  1. 查找二级缓存 (如果配置开启)。
  2. 如果二级缓存未命中,查找一级缓存
  3. 如果一级缓存也未命中,查询数据库
  4. 数据库查询结果会放入一级缓存
  5. 如果配置了二级缓存,当 SqlSession 提交 (commit()) 或关闭 (close()) 时,一级缓存中的特定数据会刷新到二级缓存中。
### 三级标题:MyBatis 一级缓存二级缓存区别 MyBatis 提供了一级缓存二级缓存两种机制,用于优化查询性能,但它们在作用范围、生命周期使用方式上存在显著差异。 一级缓存是默认开启的,作用范围局限于单个 `SqlSession`。在同一个会话中,如果多次执行相同的查询语句,MyBatis 会从一级缓存中直接返回结果,而不会再次访问数据库。一旦该会话关闭,一级缓存中的数据也会被清除。一级缓存的范围可以通过 `localCacheScope` 配置项进行调整,例如设置为 `STATEMENT` 时,每次执行完语句后都会清空缓存[^3]。 二级缓存则是基于全局的缓存机制,需要手动开启配置。其作用范围为 `SqlSessionFactory`,可以被多个 `SqlSession` 共享。在某个会话中查询到的数据,在会话关闭后会被保存到二级缓存中,其他会话在后续查询时可以直接从二级缓存中获取数据。二级缓存的生命周期与 `SqlSessionFactory` 相同,因此它的生命周期更长,适用范围更广[^2]。 在查询数据时,MyBatis缓存优先级为:二级缓存一级缓存 → 数据库。也就是说,当执行查询操作时,MyBatis 首先会尝试从二级缓存中获取数据,如果未命中,则继续查找一级缓存,最后才会访问数据库[^1]。 为了进一步扩展二级缓存的功能,可以集成第三方缓存组件,例如 Redis。通过引入 `mybatis-redis` 依赖,并在 Mapper 文件中配置 `<cache>` 标签,可以将 MyBatis二级缓存存储到 Redis 中,从而实现分布式缓存的效果。以下是一个简单的配置示例: ```xml <mapper namespace="com.example.UserMapper"> <cache type="org.mybatis.caches.redis.RedisCache" /> </mapper> ``` 此外,还需配置 `redis.properties` 文件以指定 Redis 的连接信息: ``` host=localhost port=6379 password= database=0 ``` 这种方式不仅提升了缓存的性能,还支持跨节点的数据共享,适用于大规模分布式系统[^4]。 --- ###
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

冰糖心书房

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值