mybaits的一级缓存与二级缓存

一 mybaits的一级缓存

1.1 一级缓存

它指的是mybaits中sqlsession对象的缓存,当我们只需查询之后,查询的结果会同事存入到sqlsession,sqlSession为我们提供了一块区域用于存储,该区域的结构是一个map,当我们再次查询同样的数据,mybaits会先去sqlsession中查询是否有数据,有的话直接拿出来使用,否则重新查询数据库。
当sqlsession对象消失时,mybaits的一级缓存也就消失了。

 执行了两次连接查询

1.2 使用缓存和不使用缓存的区别

1.3 一级缓存的流程

 1.4 一级缓存的命中条件

1.statementid,其实也就是select id的名称,对应dao层就是两个不同的方法。必须相同,才会命中。

 2.查询条件

 3. 分页参数

 

 4.sql语句

 5.不同环境,其实就是不同数据库

1.5 一级缓存的销毁

一级缓存中关闭,提交,回滚,更新,清除均能销毁一级缓存。

1.6 一级缓存的脏读

以上的理解是不对的:因为mybatis的一级缓存的生命周期是在数据库事务生命周期内,什么意思的呢,也就是mybatis的session执行close,commit,rollback清空缓存之后,才会触发mysql的一个事务机制。

 2.mysql的

 

 总结:一级缓存没有问题,放行使用,不然的话,市场上早就砸开锅了,哈哈

1.7 与spring的集成

二 mybaist的二级缓存

2.1 二级缓存

它指的是mybaits中sqlsessionFactory对象的缓存,由同一个sqlsessionFactory对象建的sqlSession共享其缓存。mybatis中的二级缓存是mapper级别的缓存,值得注意的是,不同的mapper都有一个二级缓存,也就是说,不同的mapper之间的二级缓存是互不影响的

如下图所示:

 2.2 开启二级缓存

1.让mybaits框架支持二级缓存(在sqlMapConfig.xml中配置)
2.让当前的映射文件支持二级缓存(在xxxMapper.xml中配置)
3.让当前的操作支持二级缓存(在select标签中配置)

总之,要想使某条Select查询支持二级缓存,你需要保证:

** 1. MyBatis支持二级缓存的总开关:全局配置变量参数 cacheEnabled=true**

** 2. 该select语句所在的Mapper,配置了<cache> 或<cached-ref>节点,并且有效**

** 3. 该select语句的参数 useCache=true**

如果当前的statement是不需要提交的,如select,useCache的默认值为true,flushCache默认为false。 

https://haokan.baidu.com/v?pd=wisenatural&vid=7052719699069214536

2.2.1 第一步

1.修改配置文件mybatis-config.xml加入<setting name="cacheEnabled"value="true"/>,全局配置参数,需要时再设置

cacheEnabled 介绍

描述 : cacheEnabled  ;允许值: 对在此配置文件下的所有cache 进行全局性开/关设置。

默认值 (true/false): true 

2.2.2 第二步

2.在mapper.xml中开启二缓存,mapper.xml下的sql执行完成会存储到它的缓存区,如:

注意:开启缓存的弊端是数据没有实时性,当数据库中的数据一旦修改,查询的数据还是缓存中的数据没有实时性,对于某些需要实时性显示数据的接口我们可以设置useCache="false",设置该属性后,该接口每次查询出来都是去执行sql查询出实时性数据。 

2.2.3 第3步

3.让当前的操作支持二级缓存(在select标签中配置)

       3.1 设置useCache=false可以禁用当前select语句的二级缓存,即每次查询都会发出sql去查询,默认情况是true,即该sql使用二级缓存。

 总结:针对每次查询都需要最新的数据sql,要设置成useCache=false,禁用二级缓存。

      3.2 清空缓存

 总结:一般下执行完commit操作都需要刷新缓存,flushCache=true表示刷新缓存,这样可以避免数据库脏读。

2.3 二级缓存相关配置

注意:

在mapper的同一个namespace中,如果有其它insert、update、delete操作数据后需要刷新缓存,如果不执行刷新缓存会出现脏读, 设置statement配置中的flushCache=”true” 属性,默认情况下为true,即刷新缓存,如果改成false则不会刷新。使用缓存时如果手动修改数据库表中的查询数据会出现脏读。

(1)当为select语句时:

flushCache默认为false,表示任何时候语句被调用,都不会去清空本地缓存和二级缓存

useCache默认为true,表示会将本条语句的结果进行二级缓存。

(2)当为insert、update、delete语句时:

flushCache默认为true,表示任何时候语句被调用,都会导致本地缓存和二级缓存被清空

useCache属性在该情况下没有。

当为select语句的时候,如果没有去配置flushCache、useCache,那么默认是启用缓存的,所以,如果有必要,那么就需要人工修改配置。

注意红色部分的字体,博主测试发现,对于statement配置中的flushCache="true"属性,它的默认值有两种情况。其一,如果当前的statement是需要提交的,

如insert、update、delete,flushCache默认为true;其二,如果当前的statement是不需要提交的,如select,flushCache默认为false。

3.所有的pojo,javabean都要实现序列化serialiale

https://blog.csdn.net/qq_35673617/article/details/80543873

2.4 二级缓存的更新策略

MyBatis自身提供了丰富的,并且功能强大的二级缓存的实现,它拥有一系列的Cache接口装饰者,可以满足各种对缓存操作和更新的策略

对于每个Cache而言,都有一个容量限制,MyBatis各供了各种策略来对Cache缓存的容量进行控制,以及对Cache中的数据进行刷新和置换。MyBatis主要提供了以下几个刷新和置换策略:

** LRU:(Least Recently Used)**,最近最少使用算法,即如果缓存中容量已经满了,会将缓存中最近做少被使用的缓存记录清除掉,然后添加新的记录;

FIFO:(First in first out),先进先出算法,如果缓存中的容量已经满了,那么会将最先进入缓存中的数据清除掉;

​ ** Scheduled:指定时间间隔清空算法,该算法会以指定的某一个时间间隔将Cache**缓存中的数据清空;

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

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

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

如下例子:

<cache  eviction="FIFO" flushInterval="60000"  size="512" readOnly="true"/>
这个更高级的配置创建了一个 FIFO 缓存,并每隔 60 秒刷新,存数结果对象或列表的 512 个引用,而且返回的对象被认为是只读的,因此在不同线程中的调用者之间修改它们会导致冲突。可用的收回策略有, 默认的是 LRU:

1.      LRU – 最近最少使用的:    会将缓存中最近做少被使用的缓存记录清除掉,然后添加新的记录。

2.      FIFO – 先进先出:按对象进入缓存的顺序来移除它们。

3.      SOFT – 软引用:移除基于垃圾回收器状态和软引用规则的对象。

4.      WEAK – 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。

查看schedulecache,默认的clearInterval默认为1hour

https://www.jianshu.com/p/b8fa01332cdd

2.5 mybaits二级缓存与一级缓存的顺序

请注意,如果你的MyBatis使用了二级缓存,并且你的Mapperselect语句也配置使用了二级缓存,那么在执行select查询的时候,MyBatis会先从二级缓存中取输入,其次才是一级缓存,即MyBatis查询数据的顺序是:

​ ** 二级缓存 ———> 一级缓存——> 数据库**

2.6 二级缓存的命中因素

2.7 缓存的产生

从图上可以看到值session只有执行close,commit等操作才会查询二级缓存。

2.8 二级缓存的声明周期以及创建与销毁

 在二级缓存中,session的close,commit,会产生二级缓存;rollback,clearcache既不会产生二级缓存也不会销毁二级缓存,update可以销毁二级缓存

 2.9 二级缓存的应用场景

对于访问多的查询请求且用户对查询结果实时性要求不高,此时可采用mybatis二级缓存技术降低数据库访问量,提高访问速度,业务场景比如:耗时较高的统计分析sql、电话账单查询sql等。

         实现方法如下:通过设置刷新间隔时间,由mybatis每隔一段时间自动清空缓存,根据数据变化频率设置缓存刷新间隔flushInterval,比如设置为30分钟、60分钟、24小时等,根据需求而定。


2.10 mybaits的局限性

mybatis二级缓存对细粒度的数据级别的缓存实现不好对同时缓存较多条数据的缓存,比如如下需求:对商品信息进行缓存,由于商品信息查询访问量大,但是要求用户每次都能查询最新的商品信息,此时如果使用mybatis的二级缓存就无法实现当一个商品变化时只刷新该商品的缓存信息而不刷新其它商品的信息,因为mybaits的二级缓存区域以mapper为单位划分,当一个商品信息变化会将所有商品信息的缓存数据全部清空。解决此类问题需要在业务层根据需求对数据有针对性缓存。需要使用三级缓存

三 一级缓存与二级缓存的区别联系

3.1 缓存流程比较

每个session都有自己的缓存

所有的session共用一个cache

总结:二级缓存与一级缓存区别在于二级缓存的范围更大,多个sqlSession可以共享一个mapper中的二级缓存区域。mybatis是如何区分不同mapper的二级缓存区域呢?它是按照不同mapper有不同的namespace来区分的,也就是说,如果两个mapper的namespace相同,即使是两个mapper,那么这两个mapper中执行sql查询到的数据也将存在相同的二级缓存区域中。

3.2 缓存整体比较

四 自定义cache

4.1 自定义rediscache+mybaits

我们可以自定义一个rediscache,里面设置put,get,remove等方法,见

https://www.imooc.com/video/21498

需要注意的地方是:自定义了缓存,原先mybaits的二级缓存的配置不能应用自定义缓存,不起作用。

五 mybaits的二级缓存案例

5.1 案例架构图

流程:

sqlSession1去查询用户id为1的用户信息,查询到用户信息会将查询数据存储到该UserMapper的二级缓存中。
如果SqlSession3去执行相同 mapper下sql,执行commit提交,则会清空该UserMapper下二级缓存区域的数据。
sqlSession2去查询用户id为1的用户信息,去缓存中找是否存在数据,如果存在直接从缓存中取出数据。


 

@Test
public void testCache2() throws Exception {
    SqlSession sqlSession1 = sqlSessionFactory.openSession();
    SqlSession sqlSession2 = sqlSessionFactory.openSession();
    SqlSession sqlSession3 = sqlSessionFactory.openSession();
    // 创建代理对象
    UserMapper userMapper1 = sqlSession1.getMapper(UserMapper.class);
    // 第一次发起请求,查询id为1的用户
    User user1 = userMapper1.findUserById(1);
    System.out.println(user1);  
    //这里执行关闭操作,将sqlsession中的数据写到二级缓存区域
    sqlSession1.close();

    //sqlSession3用来清空缓存的,如果要测试二级缓存,需要把该部分注释掉
    //使用sqlSession3执行commit()操作
    UserMapper userMapper3 = sqlSession3.getMapper(UserMapper.class);
    User user  = userMapper3.findUserById(1);
    user.setUsername("倪升武");
    userMapper3.updateUser(user);
    //执行提交,清空UserMapper下边的二级缓存
    sqlSession3.commit();
    sqlSession3.close();

    UserMapper userMapper2 = sqlSession2.getMapper(UserMapper.class);
    // 第二次发起请求,查询id为1的用户
    User user2 = userMapper2.findUserById(1);
    System.out.println(user2);

    sqlSession2.close();

}

 我们先把sqlSession3部分注释掉来测试一下二级缓存的结果:

 当我们把sqlSession3部分加上后,再测试一下二级缓存结果:

 https://blog.csdn.net/eson_15/article/details/51669608

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值