Mybatis 缓存机制

MyBatis 缓存是用来减少数据库查询次数,提高性能的一种机制。MyBatis 提供了两级缓存:一级缓存和二级缓存。

1. 一级缓存(Session级缓存)

  • 作用范围:一级缓存是 MyBatis 默认开启的缓存,作用于一个 SqlSession 内部,查询结果会缓存到 SqlSession 的本地缓存中,只要在同一个 SqlSession 中发起相同的查询,MyBatis 会直接返回缓存中的结果,而不会重新查询数据库。

  • 特点

    • 仅限当前 SqlSession 生命周期内有效。
    • 会话关闭时,一级缓存会被清除。
    • 如果在同一个 SqlSession 内执行相同的查询,MyBatis 会直接从缓存中获取结果,避免了重复查询。
  • 注意事项

    • 如果执行了 clearCache() 方法或者手动关闭了 SqlSession,缓存会被清空。
    • 通过 flushStatements() 方法也会清空缓存。

因为现在开发不会手动获取 sqlSession 来执行查询,都是基于 mapper 接口来查询的,所以简单点可以这样理解:

如果一个方法使用了事物注解,这个方法里查询数据库的数据会被缓存起来,如果再执行相同的查询会到缓存中取数据。如果这个方法没有使用事物注解,一级缓存就不会生效了

@Transactional
public void find(){

  // 第一次查询:到数据库查询并缓存起来(事物未结束,sqlSession 就不会关闭,一级缓存基于 sqlSession)
  userMapper.findById(1L);
  
  // 第二次查询:直接查缓存,不会再发 sql 了
  userMapper.findById(1L);

}

// @Transactional 如果这个方法没有加事物,两个方法都会发sql到数据库查询(因为每个查询都会打开一个新的 sqlSession)
public void find(){

  userMapper.findById(1L);
  
  userMapper.findById(1L);

}

@Transactional
public void find(){

  // 第一次查询,会到数据库查询
  userMapper.findById(1L);
  
  // 删除 7 号(只要同一个sqlSession 中执行了增删改,一级缓存会失效,哪怕修改的同一条数据)
  userMapper.deleteById(7L);
  
  // 第二次查询,也会到数据库查询
  userMapper.findById(1L);

}

2. 二级缓存(Mapper级缓存)

  • 作用范围:二级缓存是跨 SqlSession 共享的缓存,作用于不同 SqlSession 之间的缓存。二级缓存由 SqlSessionFactory 管理,不会随着 SqlSession 的关闭而清空,缓存的生命周期长于 SqlSession。

  • 开启二级缓存

    1. 在 MyBatis 配置文件中开启二级缓存:

      <settings>
        <setting name="cacheEnabled" value="true"/>
      </settings>
      
    2. 在对应的 Mapper 文件中,使用 <cache> 标签启用:

      <mapper namespace="com.example.Mapper">
          <cache/>
      </mapper>
      
  • 特点

    • 存储在内存中,多个 SqlSession 可以共享。
    • 当不同的 SqlSession 查询相同的数据时,可以直接从二级缓存中获取。
    • 只要缓存有效,查询时会先尝试从二级缓存中获取数据,若缓存中没有则查询数据库并将结果存入缓存。
  • 缓存的清除

    • 二级缓存的清除机制较为复杂,一般会在以下几种情况下清除:
      • 在执行增、删、改操作时,MyBatis 会清除相关 Mapper 的缓存。
      • 可以通过配置 flushInterval 来指定缓存的刷新间隔。
  • 缓存存储方式

    • MyBatis 默认使用 PerpetualCache(一个基于 HashMap 的缓存实现)来存储数据。
    • 你也可以自定义缓存实现,例如使用 Ehcache 或 Redis 等外部缓存系统。

3. 缓存的刷新策略

  • 缓存的更新:MyBatis 会自动对更新操作(insert, update, delete)进行缓存的刷新。对于查询操作,缓存会在查询时根据需要进行刷新。

  • 缓存的失效

    • 如果你修改了数据库中的数据,而不通过 MyBatis 来执行修改操作,那么缓存就会失效,因此最好通过 MyBatis 提供的增、删、改操作来确保缓存的一致性。

4. 缓存的配置示例

<mapper namespace="com.example.Mapper">
    <cache/>
    
    <select id="selectUser" resultType="com.example.User">
        SELECT * FROM users WHERE id = #{id}
    </select>
    
    <insert id="insertUser">
        INSERT INTO users (name, age) VALUES (#{name}, #{age})
    </insert>
</mapper>

在这个例子中,<cache/> 标签启用了二级缓存。

5. 使用缓存的最佳实践

  • 一级缓存

    • 使用适当的 SqlSession 生命周期,不要频繁地创建和关闭 SqlSession。
    • 在同一个事务中使用相同的 SqlSession 进行查询,以充分利用一级缓存。
  • 二级缓存

    • 启用二级缓存时,确保业务操作的缓存一致性,及时刷新缓存。
    • 对于高频率的查询,可以通过二级缓存减轻数据库的负担。
    • 使用外部缓存(如 Redis、Ehcache)时,要配置好缓存的过期时间和存储策略。

总结

  • 一级缓存:是 MyBatis 默认的缓存,存在于 SqlSession 内部,生命周期与 SqlSession 相同。
  • 二级缓存:是可配置的全局缓存,作用于多个 SqlSession,生命周期与 SqlSessionFactory 相同(基本不会用到,redis)。
  • 缓存的合理使用可以显著提高 MyBatis 性能,但要注意缓存一致性和失效机制。
posted @ 2023-07-28 11:27  CyrusHuang  阅读(55)  评论(0)    收藏  举报