mybatis的缓存机制

视频教程_免费高速下载|百度网盘-分享无限制 (baidu.com)

MyBatis 有一套灵活而强大的缓存机制,主要分为两级缓存:一级缓存(本地缓存)和二级缓存(全局缓存)。

  1. 一级缓存(本地缓存)

    • 一级缓存是 MyBatis 默认开启的缓存,它指的是在同一个 SqlSession 中执行相同 SQL 语句时,第一次查询的结果会被缓存起来,后续再执行相同的查询,直接从缓存中获取结果,而不再去查询数据库。

    • 一级缓存是基于 SqlSession 的生命周期的,当 SqlSession 关闭时,一级缓存也就失效了。

    • 一级缓存可以通过清空缓存或提交事务来手动刷新,默认情况下,只要 SqlSession执行一条语句,就会刷新,不会等到 SqlSession结束(不像二级缓存)

    • 一级缓存,在进行DML操作后,会使得缓存失效

    • 一个会话DML操作只会重置当前会话的缓存,不会重置其他会话的缓存,也就是说,其他会话缓存是不会更新的!

  2. 二级缓存(全局缓存)

    • 二级缓存是全局缓存,可以跨多个 SqlSession 共享缓存,但是只有当一个SqlSession结束之后会刷新二级缓存。所以当 SqlSession1和 SqlSession2都没结束且查询同样内容的时候,不会调用二级缓存,因为二级缓存啥都没有。

    • 二级缓存需要在 MyBatis 的配置文件中进行配置启用,并且需要在映射文件中指定哪些语句使用二级缓存。

      不希望某个方法开启缓存呢?我们可以添加useCache属性来关闭缓存:

      XML 复制代码
      <select id="getStudentBySid" resultType="Student" useCache="false">
          select * from student where sid = #{sid}
      </select>
    • 对于同一个 namespace 下的语句,如果开启了二级缓存,它们的查询结果会被缓存在一个全局的缓存区域中,当其他 SqlSession 执行相同的查询时,可以从全局缓存中获取结果。

    • 二级缓存是基于 namespace 级别的,不同的 namespace 有独立的缓存区域,它们之间不会相互影响。

    • 二级缓存默认是开启的,但需要在映射文件中明确指定 <cache/> 元素,才会对该 namespace 启用缓存。

      XML 复制代码
      <mapper namespace="com.example.BlogMapper">
        <cache
          eviction="FIFO"
          flushInterval="60000"
          size="512"
          readOnly="true"/>
        
        <!-- 其他映射语句 -->
      </mapper>

      namespace="com.example.BlogMapper" :指定缓存的 namespace,即当前映射文件所属的命名空间。这个命名空间用于标识二级缓存的作用范围,是一个唯一的标识符,通常与映射器接口或者类的全路径名相对应。eviction="FIFO" :指定缓存的淘汰策略。在这里,FIFO 表示采用先进先出(First In, First Out)策略,即最先进入缓存的数据最先被淘汰。flushInterval="60000" :指定缓存刷新的时间间隔,单位为毫秒。在这里,设置为 60000 毫秒,即 60 秒,表示 MyBatis 会定期刷新缓存,清理过期的缓存项。size="512" :指定缓存的最大项数。当缓存中的项数达到设定的值时,MyBatis 会尝试清理缓存。readOnly="true" :指定是否只读。设置为 true 时,表示缓存只读,不会修改缓存中的数据,可以提高性能。默认值为 falsereadOnly="true" :指定是否只读。设置为 true 时,表示缓存只读,不会修改缓存中的数据,可以提高性能。默认值为 false

    • 二级缓存的实现是可插拔的,可以自定义缓存实现,如使用 Ehcache、Redis 等。

    • 添加了二级缓存之后,会先从二级缓存中查找数据,当二级缓存中没有时,才会从一级缓存中获取,当一级缓存中都还没有数据时,才会请求数据库

需要注意的是,缓存机制虽然能提高查询性能,但在一些更新操作(例如插入、更新、删除)时,可能会导致缓存的数据不一致。因此,在执行这类操作时,MyBatis 会自动清空相关的缓存,保证数据的正确性。开发者也可以通过手动清理缓存来控制缓存的行为。

虽然缓存机制给我们提供了很大的性能提升,但是缓存存在一个问题,我们之前在计算机组成原理中可能学习过缓存一致性问题,也就是说当多个CPU在操作自己的缓存时,可能会出现各自的缓存内容不同步的问题,而Mybatis也会这样,我们来看看这个例子:

java 复制代码
public static void main(String[] args) throws InterruptedException {
    try (SqlSession sqlSession = MybatisUtil.getSession(true)){
        TestMapper testMapper = sqlSession.getMapper(TestMapper.class);
        while (true){
            Thread.sleep(3000);
            System.out.println(testMapper.getStudentBySid(1));
        }
    }
}

我们现在循环地每三秒读取一次,而在这个过程中,我们使用IDEA手动修改数据库中的数据,将1号同学的学号改成100.

结果是依小明的sid并没有发生改变,这也证明了Mybatis的缓存在生效,因为我们是从外部进行修改,Mybatis不知道我们修改了数据 ,所以依然在使用缓存中的数据,但是这样很明显是不正确的,因此,如果存在多台服务器或者是多个程序都在使用Mybatis操作同一个数据库,并且都开启了缓存,需要解决这个问题,要么就得关闭Mybatis的缓存来保证一致性

XML 复制代码
<settings>
    <setting name="cacheEnabled" value="false"/>
</settings>

cacheEnabled 是一个全局开关,用于控制是否开启缓存。在这里,将其值设置为 false,表示禁用缓存。

XML 复制代码
<select id="getStudentBySid" resultType="Student" useCache="false" flushCache="true">
    select * from student where sid = #{sid}
</select>
  • useCache="false":这个属性用于指定是否使用缓存。在这里,useCache 被设置为 false,表示禁用缓存。即使全局缓存配置是启用的,这个查询也不会使用缓存。

  • flushCache="true":这个属性用于指定是否刷新缓存。在这里,flushCache 被设置为 true,表示在执行这个查询之前会刷新(清空)缓存。即使全局缓存是启用的,这个查询会强制刷新缓存,确保获取最新的结果。

要么就需要实现缓存共用,也就是让所有的Mybatis都使用同一个缓存进行数据存取,在后面,我们会继续学习Redis、Ehcache、Memcache等缓存框架,通过使用这些工具,就能够很好地解决缓存一致性问题

相关推荐
攻城狮的梦1 小时前
redis集群模式连接
数据库·redis·缓存
二十雨辰2 小时前
[苍穹外卖]-12Apache POI入门与实战
java·spring boot·mybatis
Amagi.4 小时前
Redis的内存淘汰策略
数据库·redis·mybatis
无休居士5 小时前
【实践】应用访问Redis突然超时怎么处理?
数据库·redis·缓存
.Net Core 爱好者5 小时前
Redis实践之缓存:设置缓存过期策略
java·redis·缓存·c#·.net
M-bao5 小时前
缓存数据和数据库数据一致性问题
数据库·缓存
AntDreamer6 小时前
在实际开发中,如何根据项目需求调整 RecyclerView 的缓存策略?
android·java·缓存·面试·性能优化·kotlin
执键行天涯8 小时前
【经验帖】JAVA中同方法,两次调用Mybatis,一次更新,一次查询,同一事务,第一次修改对第二次的可见性如何
java·数据库·mybatis
工业甲酰苯胺9 小时前
Spring Boot 整合 MyBatis 的详细步骤(两种方式)
spring boot·后端·mybatis
微刻时光9 小时前
Redis集群知识及实战
数据库·redis·笔记·学习·程序人生·缓存