Mybatis的一级缓存与二级缓存

2023-10-25 10:48:48 浏览数 (1)

我们都知道缓存,缓存的作用也都是非常的明显,为了减轻数据库的压力,有些时候查询数据的时候,会把数据存入到缓存中,等接下来相同的请求查询数据时,直接从缓存中获取数据,不用再去访问数据库了,而合理使用缓存是优化中最常见的,避免频繁操作数据库,减轻数据库的压力,同时提高系统性能。

Mybatis 的一级缓存

什么是Mybatis的一级缓存

一级缓存是 SqlSession 级别的缓存。在操作数据库时需要构造 SqlSession 对象,在对象中有一个数据结构用于存储缓存数据。

不同的 SqlSession 之间的缓存数据区域是互相不影响的。也就是他只能作用在同一个 SqlSession 中,不同的 SqlSession 中的缓存是互相不能读取的。

一级缓存的工作原理图解:

用户发起查询请求,查找某条数据,SqlSession 先去缓存中查找,是否有该数据,如果有,读取;

如果没有,从数据库中查询,并将查询到的数据放入一级缓存区域,供下次查找使用。

SqlSession 执行 commit,即增删改操作时会清空缓存。

那么清空的意义在哪,了不起都不用多说了吧,这清空的意义就是要保证我们读取的数据一定是准确的,如果你执行了增删改的操作之后,进行了 commit 之后,我们不做清空操作,那么查询的数据,肯定不是最新的,也就是会出现脏读的情况了。

如果不清空的情况下,就会有这种情况,某一件商品,库存有10件,这个时候我们查看的时候,发现库存10件,然后查询之后,写入了缓存,而接下来有人下单,一口气购买了10件,数据没了,如果我们不清空缓存中的内容,那么接下来我们还是从缓存中去取数的话,取出来的数据10就是一个错误的数据了。

Mybatis 的二级缓存

Mybatis的二级缓存是什么?

二级缓存是 mapper 级别的缓存,多个 SqlSession 去操作同一个 Mappersql 语句,多个 SqlSession可以共用二级缓存,二级缓存是跨 SqlSession 的。

二级缓存原理图:

UserMapper 有一个二级缓存区域(按 namespace 划分),每个 mapper 也有自己的二级缓存区域(按 namespace 分)。

每一个 namespacemapper 都有一个二级缓存区域,如果相同两个 mappernamespace ,这两个 mapper 执行 sql 查询到数据将存在相同的二级缓存区域中。

如何开启二级缓存

如果你使用了 Mybatis 的配置文件,那么就得增加配置为:

代码语言:javascript复制
<settings>
<!-- 开启二级缓存 -->
<setting name="cacheEnabled" value="true"/>
</settings>

如果你使用的是 yml 去配置的话,那么就得在 configuration 下去开启:

代码语言:javascript复制
configuration:
    cache-enabled: true

我们看一段测试代码:

代码语言:javascript复制
@Test
public void testCache2() throws Exception {
SqlSession sqlSession1 = sqlSessionFactory.openSession();
SqlSession sqlSession2 = sqlSessionFactory.openSession();
UserMapper userMapper1 = sqlSession1.getMapper(UserMapper.class);
User user1 = userMapper1.findUserById(1);
System.out.println(user1);
sqlSession1.close();
UserMapper userMapper2 = sqlSession2.getMapper(UserMapper.class);
User user2 = userMapper2.findUserById(1);
System.out.println(user2);
sqlSession2.close();
}

日志打印:

代码语言:javascript复制
DEBUG [main] - Cache Hit Ratio [com.iot.mybatis.mapper.UserMapper]: 0.0
DEBUG [main] - Opening JDBC Connection
DEBUG [main] - Created connection 103887628.
DEBUG [main] - Setting autocommit to false on JDBC Connection
[com.mysql.jdbc.JDBC4Connection@631330c]
DEBUG [main] - ==> Preparing: SELECT * FROM user WHERE id=?
DEBUG [main] - ==> Parameters: 1(Integer)
DEBUG [main] - <== Total: 1
User [id=1, username=张三, sex=1, birthday=null, address=null]
DEBUG [main] - Resetting autocommit to true on JDBC Connection
[com.mysql.jdbc.JDBC4Connection@631330c]
DEBUG [main] - Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@631330c]
DEBUG [main] - Returned connection 103887628 to pool.
DEBUG [main] - Cache Hit Ratio [com.iot.mybatis.mapper.UserMapper]: 0.5
User [id=1, username=张三, sex=1, birthday=null, address=null]

我们可以从打印的信息看出,两个 SqlSession,去查询同一条数据,只发起一次 select 查询语句,第二次直接从 Cache 中读取。

前面我们说到,SpringMyBatis 整合时, 每次查询之后都要进行关闭 SqlSession,关闭之后数据被清空。所以 spring 整合之后,如果没有事务,一级缓存是没有意义的。

那么如果开启二级缓存,关闭 sqlsession 后,会把该 sqlsession 一级缓存中的数据添加到 namespace 的二级缓存中。这样,缓存在 sqlsession 关闭之后依然存在。

二级缓存的使用

1. 只能在一个命名空间下使用二级缓存

由于二级缓存中的数据是基于 namespace 的,即不同 namespace 中的数据互不干扰。在多个 namespace 中存在对同一个表的操作,那么这多个 namespace 中的数据可能就会出现不一致现象。

2. 在单表上使用二级缓存

如果一个表与其它表有关联关系,那么就非常有可能存在多个 namespace 对同一数据的操作。而不同 namespace 中的数据相互干扰,所以就有可能出现多个 namespace 中的数据不一致现象。

3. 查询多于修改时使用二级缓存

在查询操作远远多于增删改操作的情况下可以使用二级缓存。因为任何增删改操作都将刷新二级缓 存,对二级缓存的频繁刷新将降低系统性能。

0 人点赞