mybatis一级缓存和二级缓存使用详解

本文涉及的产品
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
云数据库 Tair(兼容Redis),内存型 2GB
简介: 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会话提交或者关闭时才会将一级缓存刷新到二级缓存中;

开启二级缓存后,用户查询时,会先去二级缓存中找,找不到在去一级缓存中找,然后才去数据库查询;

119.png

118.png




三、一级缓存的使用


spring开启事务管理的方法在方法执行过程中只会创建一个sqlsession。

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


不开启事务:

@Override
public GoodsStock getStock(Integer goodsId) {
    log.info("第1次查询");
    GoodsStock goodsStock = goodsStockMapper.getStock(goodsId);
    log.info("第2次查询");
    goodsStock = goodsStockMapper.getStock(goodsId);
    return goodsStock;
}


执行结果:

117.png


开启事务:

@Override
@Transactional(rollbackFor = Exception.class)
public GoodsStock getStock(Integer goodsId) {
    log.info("第1次查询");
    GoodsStock goodsStock = goodsStockMapper.getStock(goodsId);
    log.info("第2次查询");
    goodsStock = goodsStockMapper.getStock(goodsId);
    return goodsStock;
}


执行结果:

116.png

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


关闭一级缓存:

在配置文件中添加如下属性

#一级缓存关闭,默认值SESSION,表示开启一级缓存,statement表示关闭一级缓存

mybatis.configuration.local-cache-scope=statement


再次请求:

115.png

通过添加配置关闭mybatis的一级缓存后,第2次查询打印了SQL,进行了查库操作。


说实话,一级缓存比较鸡肋,一般在同一个方法内,很少会进行重复的查询,在实际业务中的真实使用场景暂时没有想到。目前的实际意义更多的是为mybatis的二级缓存做一个铺垫。


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

1、一般在一个方法内需要用相同条件查询多次的场景其实非常少见,因为方法体内对象都是可见共享的,没必要再次进行查询。

2、如果需要再次查询判断之前的查询结果是否已经出现变更,这时除了需要关闭一级缓存外,还需要注意事务的隔离级别。

比如,

如果事务隔离级别为可重复读时,在本事务中尽管执行多次查询,由于会对查询的数据在事务范围内加读锁,所以查询的数据一定是相同的,没有重复查下的必要。

如果事务隔离级别为读已提交,需要进行重复查询判断数据的变化情况,那么需要关闭mybatis的一级缓存。


不过,推荐在需要多次查询做数据比对的场景,最好还是关闭事务,这样就可以保障每次查询都进行查库操作,获取最新的数据。


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

@Override
@Transactional(rollbackFor = Exception.class,isolation=Isolation.REPEATABLE_READ,readOnly = true)
public GoodsStock getStock(Integer goodsId) {
    log.info("第1次查询");
    //由于是REPEATABLE_READ可重复读隔离级别,在事务范围内会对读取到的数据加锁,所以读取到的数据一定不会发生变更,没有再次查询的必要
    GoodsStock goodsStock = goodsStockMapper.getStock(goodsId);
    log.info("第2次查询");
    goodsStock = goodsStockMapper.getStock(goodsId);
    return goodsStock;
}


事务为读已提交READ_COMMITTED,由于读锁会立即释放,导致事务范围内已经查询到的数据可能被其他事务修改,如果需要重复查库判断数据变化,那么必须关闭mybatis的一级缓存,不然2次查询不会进行查库操作。

如果在1次查询和2 次查询中的逻辑处理有一个 insert 、update或delete 操作,那么之前查询的所有缓存都会清空。

2次查询也会进行查库操作。


@Override
@Transactional(rollbackFor = Exception.class,isolation=Isolation.READ_COMMITTED,readOnly = true)
public GoodsStock getStock(Integer goodsId) {
    log.info("第1次查询");
    //由于是REPEATABLE_READ可重复读隔离级别,在事务范围内会对读取到的数据加锁,所以读取到的数据一定不会发生变更,没有再次查询的必要
    GoodsStock goodsStock1 = goodsStockMapper.getStock(goodsId);
    log.info("逻辑处理");
    log.info("第2次查询,判断变化,需要关闭mybatis的一级缓存");
    GoodsStock goodsStock2 = goodsStockMapper.getStock(goodsId);
    return goodsStock2;
}


四、二级缓存的使用


二级缓存相关的配置有三个地方:

1、mybatis-config中有一个全局配置属性,这个不配置也行,因为默认就是true。

<setting name="cacheEnabled" value="true"/>


spring boot中也可以通过配置项开启二级缓存:


#二级缓存开启
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="selectUserAndJob" resultMap="JobResultMap2" useCache="true">
      select * from lw_user
  </select>

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


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

114.png


执行结果报序列化异常,那是因为我们映射的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进行编码控制缓存,优点是控制灵活,缺点是需要编码实现。


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


总结


本文主要是针对mybatis的一级缓存和二级缓存的区别和使用进行了详细介绍。重点掌握一下方面

1、一级缓存和二级缓存的区别

2、mybatis缓存的生命周期

3、如何开启二级缓存,二级缓存什么时候失效,如何将本地的二级缓存升级到采用redis支持分布式的二级缓存?

4、二级缓存的淘汰策略有哪些

5、mybatis缓存、spring缓存和redis缓存的比较。


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

目录
相关文章
|
12月前
|
存储 缓存 芯片
让星星⭐月亮告诉你,当我们在说CPU一级缓存二级缓存三级缓存的时候,我们到底在说什么?
本文介绍了CPU缓存的基本概念和作用,以及不同级别的缓存(L1、L2、L3)的特点和工作原理。CPU缓存是CPU内部的存储器,用于存储RAM中的数据和指令副本,以提高数据访问速度,减少CPU与RAM之间的速度差异。L1缓存位于处理器内部,速度最快;L2缓存容量更大,但速度稍慢;L3缓存容量最大,由所有CPU内核共享。文章还对比了DRAM和SRAM两种内存类型,解释了它们在计算机系统中的应用。
983 1
|
3月前
|
存储 缓存 NoSQL
mybatisplus一二级缓存
MyBatis-Plus 继承并优化了 MyBatis 的一级与二级缓存机制。一级缓存默认开启,作用于 SqlSession,适用于单次会话内的重复查询;二级缓存需手动开启,跨 SqlSession 共享,适合提升多用户并发性能。支持集成 Redis 等外部存储,增强缓存能力。
|
5月前
|
缓存 Java 数据库连接
Mybatis一级缓存详解
Mybatis一级缓存为开发者提供跨数据库操作的一致性保证,有效减轻数据库负担,提高系统性能。在使用过程中,需要结合实际业务场景选择性地启用一级缓存,以充分发挥其优势。同时,开发者需注意其局限性,并做好事务和并发控制,以确保系统的稳定性和数据的一致性。
153 20
|
缓存 Java 数据库连接
mybatis复习05,mybatis的缓存机制(一级缓存和二级缓存及第三方缓存)
文章介绍了MyBatis的缓存机制,包括一级缓存和二级缓存的配置和使用,以及如何整合第三方缓存EHCache。详细解释了一级缓存的生命周期、二级缓存的开启条件和配置属性,以及如何通过ehcache.xml配置文件和logback.xml日志配置文件来实现EHCache的整合。
mybatis复习05,mybatis的缓存机制(一级缓存和二级缓存及第三方缓存)
|
7月前
|
缓存 Java 数据库连接
Mybatis一级缓存、二级缓存详讲
本文介绍了MyBatis中的查询缓存机制,包括一级缓存和二级缓存。一级缓存基于同一个SqlSession对象,重复查询相同数据时可直接从缓存中获取,减少数据库访问。执行`commit`操作会清空SqlSession缓存。二级缓存作用于同一namespace下的Mapper对象,支持数据共享,需手动开启并实现序列化接口。二级缓存通过将数据存储到硬盘文件中实现持久化,为优化性能,通常在关闭Session时批量写入缓存。文章还说明了缓存的使用场景及注意事项。
211 7
Mybatis一级缓存、二级缓存详讲
|
8月前
|
缓存 Java 数据库连接
十、MyBatis的缓存
十、MyBatis的缓存
137 6
|
9月前
|
缓存 NoSQL Java
Mybatis学习:Mybatis缓存配置
MyBatis缓存配置包括一级缓存(事务级)、二级缓存(应用级)和三级缓存(如Redis,跨JVM)。一级缓存自动启用,二级缓存需在`mybatis-config.xml`中开启并配置映射文件或注解。集成Redis缓存时,需添加依赖、配置Redis参数并在映射文件中指定缓存类型。适用于查询为主的场景,减少增删改操作,适合单表操作且表间关联较少的业务。
165 6
|
11月前
|
SQL 缓存 Java
【详细实用のMyBatis教程】获取参数值和结果的各种情况、自定义映射、动态SQL、多级缓存、逆向工程、分页插件
本文详细介绍了MyBatis的各种常见用法MyBatis多级缓存、逆向工程、分页插件 包括获取参数值和结果的各种情况、自定义映射resultMap、动态SQL
【详细实用のMyBatis教程】获取参数值和结果的各种情况、自定义映射、动态SQL、多级缓存、逆向工程、分页插件
|
10月前
|
缓存 Java 数据库连接
深入探讨:Spring与MyBatis中的连接池与缓存机制
Spring 与 MyBatis 提供了强大的连接池和缓存机制,通过合理配置和使用这些机制,可以显著提升应用的性能和可扩展性。连接池通过复用数据库连接减少了连接创建和销毁的开销,而 MyBatis 的一级缓存和二级缓存则通过缓存查询结果减少了数据库访问次数。在实际应用中,结合具体的业务需求和系统架构,优化连接池和缓存的配置,是提升系统性能的重要手段。
372 4
|
10月前
|
缓存 Java 数据库连接
MyBatis缓存机制
MyBatis提供两级缓存机制:一级缓存(Local Cache)默认开启,作用范围为SqlSession,重复查询时直接从缓存读取;二级缓存(Second Level Cache)需手动开启,作用于Mapper级别,支持跨SqlSession共享数据,减少数据库访问,提升性能。
154 1