Mybatis的缓存机制介绍

目录

1、MyBatis的缓存机制

1.1、定义

1.2、分类

1. 一级缓存

2. 二级缓存

1.3、缓存查询流程

1.4、缓存参数Cache

2、一级缓存

2.1、定义

2.2、流程图

2.3、实现

3、二级缓存

3.1、定义

3.2、整体设计

3.3、实现

4、二级缓存自定义实现

5、总结


前言

        缓存就是指存在内存中的临时数据,能够减少和数据库交互的次数,提高效率

        将相同查询条件的sql语句执行一遍后得到的结果存在内存或者某种缓存介质中,当下次遇到相同的查询sql时候,不用先执行sql与数据库交互,而是直接从缓存中获取结果,减少服务器的压力;

关于mybatis的更多介绍,可参考:

关于Mybatis和JDBC的联系_jdbc和mybatis的关系-CSDN博客文章浏览阅读1k次,点赞26次,收藏28次。前言基于java的持久层框架,它内部封装了jdbc,使开发者只需要关注本身,而不需要花费精力去处理加载驱动、创建连接、创建statement等繁杂的过程。_jdbc和mybatis的关系 https://blog.csdn.net/weixin_50055999/article/details/147553601?spm=1011.2415.3001.5331

mybatis缓存包括:

⼀级缓存:将查询到的数据存储到SqlSession中。范围比较小,默认开启一级缓存

● ⼆级缓存:范围比较大,针对于整个数据库级别的, 需要在 setting 全局参数中配置开启二级缓存。

⚠️注意:缓存只针对于DQL语句,也就是缓存机制只对应select语句。


1、MyBatis的缓存机制

1.1、定义

        一级缓存是属于SqlSession对象,每次commit/close会被清空,而二级缓存可以被持久化和长期复用。

        MyBatis内部的查询操作是先查namespace Cache(二级缓存),如果没找到,再查本地localcache(SqlSession)缓存。

1.2、分类

1. 一级缓存

SqlSession级别缓存,默认开启。

  • 范围:同一个SqlSession内,相同的查询语句和参数,第二次查询时不会再去数据库,而是直接从缓存返回结果。
  • 失效条件:增删改操作(flush)、手动清理缓存、切换了SqlSession、查询条件不一样。
  • 线程隔离:不同SqlSession之间互不影响。

2. 二级缓存

Mapper级别/跨SqlSession的缓存,默认关闭。

  • 范围:同一个Mapper命名空间下,不同SqlSession可以共享缓存。
  • 失效条件:本Mapper下有更新操作或手动清理缓存。
  • 生命周期:依赖MyBatis全局配置和Mapper开启了二级缓存;
  • 持久化:可指定缓存实现(如Ehcache、Redis)。

1.3、缓存查询流程

        用户A用SqlSessionA查过某条数据,执行commit或close,这条数据同步进了二级缓存。

        用户B用SqlSessionB查询同一条数据,先查二级缓存能直接命中,无需等到A的SqlSession存活时才能复用,最大化缓存效率。

  • 查询时优先通过MappedStatement.namespace查找对应的全局 cache 对象(即二级缓存)。
  • 未命中,才查SqlSession中的 localCache(一级缓存)。
// org.apache.ibatis.executor.CacheExecutor
public <E> List<E> query(MappedStatement ms, Object parameterObject, ...) {
    Cache cache = ms.getCache();
    if (cache != null) {
        // 查二级缓存
        ...
    }
    // 一级缓存逻辑
    ...
}

        MyBatis 之所以先查二级缓存、再查一级缓存,是因为二级缓存范围更广、复用率更高。如果先查一级缓存会导致大量数据无法被复用,降低缓存命中率。

        先查全局(namespace)缓存有利于提升性能和一致性,这也是MyBatis一致性与性能设计的体现。

1.4、缓存参数Cache

1、flushInterval(刷新间隔)

        可以被设置为任意的正整数,而且它们代表一个合理的毫秒形式的时间段。默认情况是不设置,也就是没有刷新间隔,缓存仅仅调用语句时刷新。

2、size(引用数目)

        可以被设置为任意正整数,要记住你缓存的对象数目和你运行环境的可用内存资源数目。默认值是1024。

3、readOnly(只读)

        属性可以被设置为true或false。只读的缓存会给所有调用者返回缓存对象的相同实例。因此这些对象不能被修改。这提供了很重要的性能优势。可读写的缓存会返回缓存对象的拷贝(通过序列化)。这会慢一些,但是安全,因此默认是false。

<cache  eviction="FIFO" flushInterval="60000"  size="512" readOnly="true"/>

        这个更高级的配置创建了一个 FIFO 缓存,并每隔 60 秒刷新,存数结果对象或列表的 512 个引用,而且返回的对象被认为是只读的,因此在不同线程中的调用者之间修改它们会导致冲突。

可用的收回策略有,默认的是 LRU:

LRU – 最近最少使用的:移除最长时间不被使用的对象。
FIFO – 先进先出:按对象进入缓存的顺序来移除它们。
SOFT – 软引用:移除基于垃圾回收器状态和软引用规则的对象。
WEAK – 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。


2、一级缓存

2.1、定义

  一级缓存区域是根据 SqlSession 为单位划分的。 每次查询会先从缓存区域找,如果找不到从数据库查询,查询到数据将数据写入缓存。

         Mybatis 内部存储缓存使用一个 HashMap,key 为 hashCode+sqlId+Sql 语句。value 为 从查询出来映射生成的 java 对象 sqlSession 执行 insert、update、delete 等操作 commit 提交后会清空缓存区域。

2.2、流程图

如下图所示:

2.3、实现

假设有如下Mapper:

public interface UserMapper {
    User selectUserById(int id);
}

代码演示:

SqlSession session = sqlSessionFactory.openSession();
UserMapper mapper = session.getMapper(UserMapper.class);

// 第一次查询,发送SQL
User user1 = mapper.selectUserById(1);

// 第二次相同条件查询,不发送SQL,直接从(一级)缓存返回
User user2 = mapper.selectUserById(1);

System.out.println(user1 == user2); // true(同个对象)

session.close();

注意:

  • 换成不同的SqlSession,不共享一级缓存。
  • 在同一个SqlSession内,若执行了insert/update/delete,缓存会清空。delete

3、二级缓存

3.1、定义

        二级缓存是mapper级别的缓存,多个SqlSession去操作同一个Mapper的sql语句,多个SqlSession去操作数据库得到数据会存在二级缓存区域,多个SqlSession可以共用二级缓存,二级缓存是跨SqlSession的。

3.2、整体设计

MyBatis的缓存机制整体设计以及二级缓存的工作模式。

如下:

3.3、实现

1. 开启全局二级缓存功能

关于mybatis-config.xml如下:

<settings>
    <setting name="cacheEnabled" value="true"/>
</settings>

2. Mapper.xml中

开启二级缓存

<mapper namespace="com.example.dao.UserMapper">
    <cache/> <!-- 一行即可开启这个mapper的二级缓存 -->
    <select id="selectUserById" resultType="User">
        SELECT id, name FROM user WHERE id = #{id}
    </select>
</mapper>

3. 代码示例

// 会话1
SqlSession session1 = sqlSessionFactory.openSession();
UserMapper mapper1 = session1.getMapper(UserMapper.class);
// 第一次查询,查数据库
User user1 = mapper1.selectUserById(1);
// 一级缓存存在session1中
session1.close(); // 提交一级缓存到二级缓存

// 会话2
SqlSession session2 = sqlSessionFactory.openSession();
UserMapper mapper2 = session2.getMapper(UserMapper.class);
// 第二次查询,先查二级缓存,如果有则不会去数据库
User user2 = mapper2.selectUserById(1);
session2.close();

System.out.println(user1 == user2); // false(对象地址不同,但数据是一样的)

常见注意点

  • 二级缓存存储是序列化的数据(所以对象不是同一引用)。
  • 任何insert/update/delete操作会刷新相关 namespace 的二级缓存。
  • 查询参数、SQL变化都可能被认为是不同key,不会命中。

4、二级缓存自定义实现

可以指定缓存实现,如:

<cache type="org.mybatis.caches.ehcache.EhcacheCache"
       eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>

也可自己实现 Cache 接口接入Redis等。


5、总结

  • 一级缓存:SqlSession 级,默认开启,生命周期短,线程不共享;
  • 二级缓存:Mapper(namespace)级,需配置开启,多个会话共享,默认内存存储,支持第三方缓存实现。
  • 实际应用建议:对于高频读多写少业务可考虑开启二级缓存,并做好缓存失效设计;写多读少场景/强一致要求则慎用二级缓存。

参考文章:

1、mybatis缓存机制-CSDN博客文章浏览阅读4.5k次,点赞11次,收藏42次。本文详细介绍了MyBatis的一级缓存和二级缓存,包括它们的作用域、特点、配置及使用场景。一级缓存默认开启,作用于同一SqlSession,而二级缓存可跨Session共享,需要手动配置。在读多写少的场景下,合理使用二级缓存能有效提升查询效率。同时,文章强调了序列化在二级缓存中的重要性,并通过实例展示了不同配置下的缓存行为。 https://blog.csdn.net/qq_44286009/article/details/125198442?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522e7fa2dd1f64664b226598806bdddb4db%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=e7fa2dd1f64664b226598806bdddb4db&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~top_positive~default-1-125198442-null-null.142^v102^control&utm_term=mybatis%E7%9A%84%E7%BC%93%E5%AD%98%E6%9C%BA%E5%88%B6&spm=1018.2226.3001.4187

2、 Mybatis的缓存机制-CSDN博客文章浏览阅读2.3k次,点赞29次,收藏35次。mybatis提供查询缓存,用于减轻数据压力,提高数据库性能。mybaits提供一级缓存,和二级缓存。一级缓存是基于SqlSession的作用范围,而二级缓存是基于mapper的namespace作用范围的。_mybatis的缓存机制 https://blog.csdn.net/qq_45794852/article/details/139259512?ops_request_misc=&request_id=&biz_id=102&utm_term=mybatis%E7%9A%84%E7%BC%93%E5%AD%98%E6%9C%BA%E5%88%B6&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduweb~default-0-139259512.142^v102^control&spm=1018.2226.3001.4187

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值