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等缓存框架,通过使用这些工具,就能够很好地解决缓存一致性问题

相关推荐
gwf21611 分钟前
SSD读写速度深度解析:顺序读写vs随机读写、IOPS、延迟,你的硬盘性能到底怎么看?
git·嵌入式硬件·缓存·github·智能硬件
番茄炒鸡蛋加糖3 小时前
缓存三大问题
缓存
江晓鱼未暖7 小时前
十七、Redis 核心原理与架构详解
大数据·数据库·数据仓库·redis·缓存·架构
小罗水8 小时前
第13章 Redis 缓存、幂等锁与任务状态
数据库·redis·缓存
热心市民lcj8 小时前
Spring Boot 整合 Caffeine 本地缓存实战
spring boot·后端·缓存
zx1154509 小时前
【热点 Key 探测与加长缓存 TTL 实战】
缓存
momo21 小时前
Java+WebAI黑马知识点
java·spring boot·mybatis
toooooop81 天前
如何用 ss + ps 精准定位本机 Redis 的“隐形”消费者?
linux·数据库·redis·缓存
软萌萌的11 天前
C#中的多级缓存架构设计与实现深度解析
缓存·c#·wpf
snow@li1 天前
MyBatis:动态 SQL 全景梳理
数据库·sql·mybatis