首页 > 其他分享 >mybatis缓存

mybatis缓存

时间:2022-09-19 14:15:47浏览次数:91  
标签:缓存 一级 开启 查询 二级缓存 mybatis

引用于 https://blog.csdn.net/w1014074794/article/details/125563231

mybatis缓存

一、概念说明

myabtis的缓存分为一级缓存和二级缓存,默认开启一级缓存,关闭二级缓存,
一级缓存时sqlSession级别,二级缓存是namespace级别。

 

1、一级缓存

mybatis的一级缓存默认是开启的,它在一个sqlSession会话里面的所有查询操作都会保存到缓存中,一般来说一个请求中的所有增删改查操作都是在同一个sqlSession里面的,所以我们可以认为每个请求都有自己的一级缓存,如果同一个sqlSession会话中2个查询中间有一个 insert 、update或delete 语句,那么之前查询的所有缓存都会清空。

使用MyBatis开启一次和数据库的会话,MyBatis会创建出一个SqlSession对象表示一次数据库会话,在对数据库的一次会话中, 有可能会反复地执行完全相同的查询语句,每一次查询都会去查一次数据库,为了减少资源浪费,mybaits提供了一种缓存的方式(一级缓存)。

一级缓存失效:
如果同一个sqlSession会话中2 个查询中间有一个 insert 、update或delete 语句,那么之前查询的所有缓存都会清空。因为每次增删改操作都有可能会改变原来的数据,所以必须刷新缓存;

 

2、二级缓存

二级缓存是全局的,也就是说;多个请求可以共用一个缓存,二级缓存需要手动开启。

二级缓存针对的是同一个namespace,所以建议是在单表操作的Mapper中使用,或者是在相关表的Mapper文件中共享同一个缓存。

一级缓存无过期时间,只有生命周期,缓存会先放在一级缓存中,当sqlSession会话提交或者关闭时才会将一级缓存刷新到二级缓存中;开启二级缓存后,用户查询时,会先去二级缓存中找,找不到在去一级缓存中找,然后才去数据库查询;

二级缓存失效:
所有的update操作(insert,delete,uptede)都会触发缓存的刷新,从而导致二级缓存失效,所以二级缓存适合在读多写少的场景中开启。

二级缓存过期时间:
需要注意的是,并不是key-value的过期时间,而是这个cache的过期时间,是flushInterval,意味着整个清空缓存cache,所以不需要后台线程去定时检测。
每当存取数据的时候,都有检测一下cache的生命时间,默认是1小时,如果这个cache存活了一个小时,那么将整个清空一下。

 

3、比较

  • 开启方式
    一级缓存是默认开启的,二级缓存默认关闭。

  • 作用范围
    一级缓存是会话级别的缓存,即sqlSession级别,会话结束,清除会话中的缓存数据,实际代码中通过通过开启事务让多个数据库操作共享一个sqlSession。
    二级缓存: 全局级别,也叫namespace级别,会话结束,缓存依然存在,多个请求可以共享缓存数据。

  • 缓存位置
    一级缓存由于是sqlSession级别,本质上是在JVM中创建一个Map集合对象保存缓存数据,所以缓存数据保留的地方是本地JVM内存中。
    二级缓存默认也是保存在JVM中,但是可以通过配置将缓存数据保存到第三方缓存中,比如ehcache、redis。保存在redis这些的分布式缓存中,能提供更好的分布式场景的支持。

  • 缓存过期
    一级缓存无过期时间,只有生命周期,缓存会先放在一级缓存中,当sqlSession会话提交或者关闭时才会将一级缓存刷新到二级缓存中;开启二级缓存后,用户查询时,会先去二级缓存中找,找不到在去一级缓存中找,然后才去数据库查询;
    二级缓存的过期时间默认是1小时,如果这个cache存活了一个小时,那么将整个清空一下。需要注意的是,并不是key-value的过期时间,而是这个cache的过期时间,是flushInterval,意味着整个清空缓存cache,所以不需要后台线程去定时检测,每当存取数据的时候,都有检测一下cache的生命时间。

 
小结:
一级缓存的作用在我看来在实际业务场景中作用真的非常有限,因为需要在一个事务方法中重复查询的需求场景真的太少,而且由于Mysql数据库的MVCC机制以及事务隔离机制-可重复读的能力,会导致同一个事务方法内多次执行相同的查询必定会得到相同的结果,所以在事务范围内的重复查询基本没什么实际作用。
设计一级缓存设计的意义,可能更多的是为二级缓存的实现做铺垫。
所以,如果关闭了mybatis的一级缓存,二级缓存将不会生效。

二、mybatis缓存的生命周期

  • mybatis的查询缓存会先放在一级缓存中,当sqlSession会话提交或者关闭时才会将一级缓存刷新到二级缓存中;
  • 开启二级缓存后,用户查询时,会先去二级缓存中找,找不到在去一级缓存中找,然后才去数据库查询;

三、一级缓存的使用

spring开启事务管理的方法在方法执行过程中只会创建一个sqlsession。
这是因为事务管理下的sql执行方式是BATCH,只会与数据库交互一次,一次执行完所有的sql,所以只会创建一个sqlsession;

不开启事务

@Override
public List<Persons> getPersonList() {
    System.out.println("第一次查询");
    List<Persons> personList = personsMapper.getPersonList();
    System.out.println("第二次查询");
    personList = personsMapper.getPersonList();
    return personList;
}

执行结果:

 

开启事务:

@Override
@Transactional(rollbackFor = Exception.class)
public List<Persons> getPersonList() {
    System.out.println("第一次查询");
    List<Persons> personList = personsMapper.getPersonList();
    System.out.println("第二次查询");
    personList = personsMapper.getPersonList();
    return personList;
}

执行结果:

可以看到,第2次查询并没有打印查询SQL,说明命中了缓存。

 

关闭一级缓存:

在配置文件中添加如下属性
一级缓存关闭,默认值session,表示开启一级缓存,statement表示关闭一级缓存

mybatis.configuration.local-cache-scope=statement

通过添加配置关闭mybatis的一级缓存后,第2次查询打印了SQL,进行了查库操作。
说实话,一级缓存比较鸡肋,一般在同一个方法内,很少会进行重复的查询,在实际业务中的真实使用场景暂时没有想到。目前的实际意义更多的是为mybatis的二级缓存做一个铺垫。

 

关于一级缓存的使用注意:

1、一般在一个方法内需要用相同条件查询多次的场景其实非常少见,因为方法体内对象都是可见共享的,没必要再次进行查询。
2、如果需要再次查询判断之前的查询结果是否已经出现变更,这时除了需要关闭一级缓存外,还需要注意事务的隔离级别。
比如:

  • 如果事务隔离级别为可重复读时,在本事务中尽管执行多次查询,由于会对查询的数据在事务范围内加读锁,所以查询的数据一定是相同的,没有重复查下的必要。
  • 如果事务隔离级别为读已提交,需要进行重复查询判断数据的变化情况,那么需要关闭mybatis的一级缓存。
  • 不过,推荐在需要多次查询做数据比对的场景,最好还是关闭事务,这样就可以保障每次查询都进行查库操作,获取最新的数据。

事务为可重复读级别REPEATABLE_READ,没有再次查询的必要,数据一定不会发生变更:

事务为读已提交READ_COMMITTED,由于读锁会立即释放,导致事务范围内已经查询到的数据可能被其他事务修改,如果需要重复查库判断数据变化,那么必须关闭mybatis的一级缓存,不然2次查询不会进行查库操作。
如果在1次查询和2次查询中的逻辑处理有一个 insert 、update或delete 操作,那么之前查询的所有缓存都会清空。2次查询也会进行查库操作。

四、二级缓存的使用

二级缓存相关的配置有三个地方:
1、通过配置项开启二级缓存:

#二级缓存开启
mybatis.configuration.cache-enabled=true    

2、在Mapper映射文件内需要配置缓存标签:

<cache eviction="LRU" flushInterval="60000" size="1000"/>

配置项说明:

  • eviction 淘汰策略,LRU、FIFO
  • size 缓存个数
  • flushInterval 刷新频率,即缓存过期时间

LruCache LRU 淘汰策略缓存(默认淘汰策略) 当缓存达到上限,删除最近最少使用缓存 eviction="LRU"
FifoCache FIFO淘汰策略缓存 当缓存达到上限,删除最先入队的缓存 eviction="FIFO"

3、在select查询语句标签上配置useCache属性,如下:

<select id="getPersonList" resultMap="BaseResultMap" useCache="true">
    select * from persons where sex = '女'
</select>

以上配置第1点是默认开启的,也就是说我们只要配置第2点就可以打开二级缓存了,而第3点是当我们需要针对某一条语句来配置二级缓存的时候则可以使用。

4、开启二级缓存后,映射的pojo对象需要实现序列化接口

执行结果报序列化异常,那是因为我们映射的pojo对象未实现序列化接口,说明我们从缓存数据中读取数据需要进行反序列化,这是因为mybatis的二级缓存的缓存介质有多种多样,而并不一定是在内存中,所以需要我们对pojo对象进行序列化,只要实现序列化接口即可。

五、自定义二级缓存

默认的二级缓存也是存储在本地缓存,对于微服务下是可能出现脏读的情况的,这时可能会需要自定义缓存,比如利用redis来存储缓存,而不是存储在本地内存当中。

MyBatis官方也提供了第三方缓存的支持引入pom文件:

<dependency>
  <groupId>org.mybatis.caches</groupId>
  <artifactId>mybatis-redis</artifactId>
  <version>1.0.0-beta2</version>
</dependency>

然后缓存配置如下:

<cache type="org.mybatis.caches.redis.RedisCache"></cache>

然后在默认的resource路径下新建一个redis.properties文件:

host=localhost
port=6379

六、mybatis缓存、spring缓存和redis缓存的使用比较

  • mybatis缓存——dao层的缓存,主要是对select查库操作的结果进行缓存。
  • spring缓存——方法级别的缓存,主要通过在方法上添加缓存注解@CachePut、@CacheEvict来实现。
  • redisTemplate缓存——代码级别的缓存,主要是在方法体内部通过redisTemplate模板类编码控制缓存读写。

mybatis缓存只能对sql查询结果进行缓存,spring 缓存只能对方法执行结果进行缓存,不能对方法执行过程中的中间对象进行缓存。这两者的优点都是使用简单,只需简单的配置或添加注解即可实现缓存,基本不需要编码。
缺点是缓存的控制范围都是方法级别,不能缓存方法执行过程中的中间对象,有一定的使用局限性。

而通过redisTemplate进行编码控制缓存,优点是控制灵活,缺点是需要编码实现。

实际项目中大家可以根据需要选择怎么使用缓存,但是要注意,针对单一方法,缓存不要混用,容易造成逻辑混乱,定位问题困难等问题。

总结

1、一级缓存和二级缓存的区别
2、mybatis缓存的生命周期
3、如何开启二级缓存,二级缓存什么时候失效,如何将本地的二级缓存升级到采用redis支持分布式的二级缓存?
4、二级缓存的淘汰策略有哪些
5、mybatis缓存、spring缓存和redis缓存的比较。

mybatis的一级缓存是为了二级缓存做铺垫,如果采用二级缓存,推荐升级到采用redis存储缓存数据,这样能更好的支持分布式。

标签:缓存,一级,开启,查询,二级缓存,mybatis
From: https://www.cnblogs.com/zjh0420/p/16707486.html

相关文章

  • mybatis-plus修改时将字段(比如备注)的内容清空(取消字段的空值过滤)
     4110次阅读 it • bug及解决方案 2019-12-09这次遇到的是修改问题,我想将备注改为空,或者说把以前的备注删掉。可是mybatisplus已经做了空值过滤,也就是如果你不......
  • mybatis-plus分页失效原因
    mybatis-plus分页失效原因一、在启动类添加如下配置@SpringBootApplication@MapperScan("com.**.mapper")publicclassDemoApplication{publicstaticvoidma......
  • Redis作为缓存和作为数据库的区别
    Redis又被叫做Nosql,非关系型数据库,由于Redis作用于内存上,所以又可以被当作是缓存,那么Redis当作缓存和数据库的区别就是:技术服务于业务逻辑,所以,选择Redis作为什么,还是要根......
  • Redis缓存穿透
    Redis缓存穿透:缓存穿透定义:当访问者搜索了redis也不存在,数据库也不存在的key的时候,这个时候,就会产生了缓存穿透,将查询压力放到了数据库中解决方案:使用布隆过滤器......
  • springboot集成mybatis获取插入数据的主键
    问题:我们想在插入一条数据后同时能够返回这条数据在表中的id,Mybatis提供了@SelectKey注解。student为数据表,主键自增SelectKey的四个属性:selectKey会将SELECTLAS......
  • mybatis注解之@Mapper和@MapperScan的使用
    +目录mybatis注解之@Mapper和@MapperScan方式一:使用@Mapper注解方式二:使用@MapperScan注解@MapperScan和@Mapper区别及理解作用扫描一个包扫描多个包myb......
  • SpringBoot集成Mybatis 实现InsertOrUpdate功能
    需求场景在项目开发过程中,难免会遇到这样的场景:对一张表,当数据不存在的时候,进行insert插入操作;数据存在的时候,进行update更新操作;下面就来使用Mybatis的InsertOrUpdate功......
  • java框架——MyBatis
    MyBatis什么是MyBatis?MyBaits是一款优秀的持久层框架,用于简化JDBC开发MyBaits本事Apached的一个开源项目iBatis,2010年这个项目由apachesoftwarefoundation迁移到......
  • SSM框架之Mybatis基本概念及使用方法
    SSMSpingMVC+Spring+Mybatis SpringMVC充当Servlet可以理解为SpringMVC是Spring的WEB支持Mybatis充当Dao层Sping充当一个润滑油的角色 MybatisPlus国内......
  • 一级缓存与二级缓存
    ​ /**作者:呆萌老师*☑csdn认证讲师*☑51cto高级讲师*☑腾讯课堂认证讲师*☑网易云课堂认证讲师*☑华为开发者学堂认证讲师*☑爱奇艺千人名师计划成员*在这里......