【MyBatis框架】MyBatis提供的缓存机制

MyBatis提供的缓存机制

缓存就是数据交换的缓冲区(称作:Cache),当某一硬件要读取数据时,会首先从缓存汇总查询数据,有则直接执行,不存在时从内存中获取。由于缓存的数据比内存快的多,所以缓存的作用就是帮助硬件更快的运行。缓存提供了一种存储方式,主要是用于存储数据,当客户端访问数据时可以直接访问缓存,从而减轻数据库的压力,提高数据库的性能。

数据的访问流程:

在这里插入图片描述

其中服务器主要的功能是和数据库进行数据交互,其主要操作无非是增删改查(查询操作比较多,特点是:频率高,不会改变数据),查询操作时服务器会从数据库获取数据的结果集,并不会引起数据本身的改变,而增、删、改的操作则会改变数据库中的数据。

服务端与数据库的交互是需要进行网络交互的。缓存则可以减少服务端和数据库的交互,从而减轻数据库的压力,提高数据库的性能,缓存主要是用于查询缓存。

在这里插入图片描述

缓存可以将数据库中的数据暂存,从而便于服务器的使用。不过,在使用缓存时需要注意脏数据的处理。

举例:
当第一次查询 id = 1时,会将查询到用户信息放入到缓存,这样后续如果对 id = 1 的用户进行查询就可以直接查询缓存数据。
但如果当 id = 1 的数据已经存储在缓存中后,对 id = 1 的数据进行变更(例如对用户 id = 1 的电话做了变更),从而会导致数据库中数据的改变。
当数据进行变更后,此时如果查询 id = 1 的用户,使用了缓存进行查询,缓存中就存在的是更新前的旧数据。
此时查询到的数据就称为脏数据。

缓存更新的时机:

为了解决上述的脏数据问题,就需要对缓存进行更新,那么它更新的时机是什么呢?

  • 当进行查询操作时:首先访问缓存,当缓存中没有对应的数据时,会在数据库查询数据,并将数据库中查询到的数据插入到缓存中,这样后续的查询就可以直接访问缓存。
  • 当进行变更操作时(修改,删除等),会将数据变更到数据库中,此时会将对应的缓存信息清空。
  • 等待再次查询时,通过访问缓存发现没有数据,就会再次查询数据库并将数据库返回的结果放入缓存。
  • 通过该更新机制就能够保证缓存中的数据是最新的数据。

缓存的实现方式:

  • 1、在单实例服务器下,可以在当前服务代码中通过添加HashMap集合实例来充当缓存。

在这里插入图片描述

  • 不过需要注意的是:在集群服务器中,本地缓存(HashMap)无法将缓存数据共享给其他的服务器。
  • 2、在多服务器下,需要通过缓存共享的形式来缓存数据,此时就会使用缓存服务器进行处理,常见的缓存服务器代表有:Redis,memCache等。
MyBatis缓存

使用缓存可以时应用更快地获取数据,避免频繁地数据库交互,尤其是在查询越多、缓存命中率越高地情况下,使用缓存地作用就越明显。MyBatis作为持久化框架,提供了非常强大地查询缓存特性,可以非常方便地配置和定制使用。

MyBatis提供了一级缓存、二级缓存两种缓存方式,一般提到MyBatis缓存的时候,都是指二级缓存。一级缓存(也叫本地缓存)默认会开启,并且不能控制,因此很少会提到。

  • 一级缓存主要是sqlSession级别的缓存,在操作数据库时是需要构造sqlSession会话对象的,对同一个对象中的数据可以使用到缓存,不同的sqlSession之间的缓存是不同享的。
  • 二级缓存主要是Mapper级别的缓存(namespace),多个sqlSession是共享缓存的。

在这里插入图片描述

一级缓存

一级缓存是sqlSession级别的缓存,作用于在同一个sqlSession当中,在同一个sqlSession中连续执行同一个SQL语句,第一次执行的结果会放入缓存当中,第二次进行查询操作时就可以直接访问缓存结果。

  • MyBatis是默认支持一级缓存的,不需要做相关配置。
  • 下面对一级缓存进行测试。测试场景如下:
    • case1:同一个sqlSession下连续的执行同一个查询操作;
    • case2:在同一个sqlSession下,先进行查询操作,再进行变更操作,然后接着进行同一个SQL查询;
    • case3:不同的sqlsession下,进行同一个SQL查询操作;
    • 测试环境与前面博客中的测试环境相同。

case1验证:

  • 测试代码:
//case1:同一个sqlSession下连续的执行同一个查询操作
@Test
public void getStudentByID() {
    SqlSession sqlSession = sqlSessionFactory.openSession();
    //通过接口产生代理类
    StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
    //第一次执行查询操作,查询id =1
    Student student = mapper.getStudentByID(1);
    System.out.println(student);
    //第二次查询
    Student student1 = mapper.getStudentByID(1);
    System.out.println(student1);
}
  • 日志打印:

在这里插入图片描述

  • 结果:在同一个sqlSession下,连续的查询同一个SQL语句,只查询一次数据库,第二次查询即访问缓存拿到结果。

case2验证:

  • 测试代码:
//case2:在同一个sqlSession下,先进行查询,在变更操作,然后进行同一个SQL查询
@Test
public void getStudentByID() {
    SqlSession sqlSession = sqlSessionFactory.openSession();
    //通过接口产生代理类
    StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
    //第一次执行查询操作,查询id =1
    Student student = mapper.getStudentByID(1);
    System.out.println(student);

    //进行更新SID=1的学生信息
    Student student2 = new Student();
    student2.setSID(1);
    student2.setname("abc");
    mapper.updateStudent(student2);
    sqlSession.commit();
        
    //第二次查询
    Student student1 = mapper.getStudentByID(1);
    System.out.println(student1);
}
  • 日志打印:

在这里插入图片描述

  • 结果:在第一次查询之后,会将数据存入缓存中,当进行了变更操作时,将缓存清空,第二次执行同一个SQL的查询操作时,此时缓中不存在数据,就会继续查询数据库。

case3验证:

  • 测试代码:
//case3:不同的sqlsession下,同一个SQL查询操作
@Test
public void getStudentByID() {
    //创建SQLSession实例1
    SqlSession sqlSession1 = sqlSessionFactory.openSession();
    StudentMapper mapper1 = sqlSession1.getMapper(StudentMapper.class);

    //创建SQLSession实例2
    SqlSession sqlSession2 = sqlSessionFactory.openSession();
    StudentMapper mapper2 = sqlSession2.getMapper(StudentMapper.class);
    
    //第一次执行查询操作
    Student student = mapper1.getStudentByID(1);
    System.out.println(student);

    //第二次查询
    Student student1 = mapper2.getStudentByID(1);
    System.out.println(student1);
}
  • 日志打印:

在这里插入图片描述

  • 结果:在同一个sqlsession实例下,连续查询操作可以使用缓存,在不同的sqlSession实例下,缓存是不共享的。
二级缓存

二级缓存是Mapper级别的缓存,默认情况下二级缓存是关闭的。同一个Mapper下不同的sqlSession是可以共享二级缓存的,不同Mapper的缓存是相互隔离的。

二级缓存和一级缓存的区别:

  • 二级缓存范围更大,多个sqlSession可以共享一个Mapper的二级缓存区域。

二级缓存使用步骤:

  • 1、需要在全局配置文件中打开二级缓存的开关。
<settings>
    <!--开启二级缓存的开关-->
    <setting name="cacheEnabled" value="true"/>
</settings>
  • 2、将映射的pojo类实现序列化。
public class Student implements Serializable 
  • 3、在mapper配置文件中添加cache标签。
<mapper namespace="com.tulun.dao.StudentMapper">
    <!--开启本Mapper的二级缓存-->
    <cache></cache>
</mapper>
  • cache的参数说明:
<mapper namespace="com.tulun.dao.StudentMapper">
    <!--
        cache参数说明:
        flushInterval(缓存刷新间隔)单位毫秒,默认情况下不设置,在变更操作时,进行缓存刷新
        size:(引用次数)记录缓存对象的数量 默认值1024
        readOnly(只读)参数为true:false  表示缓存的数据对象实例是不能被修改
        eviction:缓存失效的策略  默认LRU
        LRU:最近最少使用的:移除最长时间不使用的
        FIFO:先进先出,按照缓存对象的顺序来淘汰
        SOFT:软引用
        WEAK:弱引用
    -->
    <cache eviction="LRU" readOnly="true" flushInterval="10000" size="12"></cache>
</mapper>
  • cache的配置禁用:
    • 在statement上配置useCache=“false”,禁用的是二级缓存。
<select id="getStudentByID" useCache="false" resultType="com.tulun.pojo.Student">
    select * from Student where SID = #{sid}
</select>

二级缓存的测试:

  • case:在同一个Mapper实例下,不同的sqlSession执行同一个查询操作。

  • 测试代码:

//case:在同一个Mapper实例下,不同的sqlsession下,同一个SQL查询操作
@Test
public void getStudentByID() {
    //创建SQLSession实例1
    SqlSession sqlSession1 = sqlSessionFactory.openSession();
    StudentMapper mapper1 = sqlSession1.getMapper(StudentMapper.class);

    //创建SQLSession实例2
    SqlSession sqlSession2 = sqlSessionFactory.openSession();
    StudentMapper mapper2 = sqlSession2.getMapper(StudentMapper.class);
        
    //第一次执行查询操作
    Student student = mapper1.getStudentByID(1);
    //执行关闭操作,将SQLSession中的数据写入到二级缓存中
    sqlSession1.close();
    System.out.println(student);

    //第二次查询
    Student student1 = mapper2.getStudentByID(1);
    System.out.println(student1);
}
  • 日志打印:

在这里插入图片描述

  • 结果:在同一个Mapper下,不同的sqlSession实例可以共享缓存。
缓存源码跟踪

在这里插入图片描述

第一次执行:

在这里插入图片描述

第二次执行:

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值