欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

深入理解MyBatis中的一级缓存与二级缓存

程序员文章站 2023-12-17 09:43:46
前言 先说缓存,合理使用缓存是优化中最常见的,将从数据库中查询出来的数据放入缓存中,下次使用时不必从数据库查询,而是直接从缓存中读取,避免频繁操作数据库,减轻数据库的压力...

前言

先说缓存,合理使用缓存是优化中最常见的,将从数据库中查询出来的数据放入缓存中,下次使用时不必从数据库查询,而是直接从缓存中读取,避免频繁操作数据库,减轻数据库的压力,同时提高系统性能。

一级缓存

一级缓存是sqlsession级别的缓存。在操作数据库时需要构造sqlsession对象,在对象中有一个数据结构用于存储缓存数据。不同的sqlsession之间的缓存数据区域是互相不影响的。也就是他只能作用在同一个sqlsession中,不同的sqlsession中的缓存是互相不能读取的。

一级缓存的工作原理:

深入理解MyBatis中的一级缓存与二级缓存

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

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

但sqlsession执行commit,即增删改操作时会清空缓存。这么做的目的是避免脏读。

如果commit不清空缓存,会有以下场景:a查询了某商品库存为10件,并将10件库存的数据存入缓存中,之后被客户买走了10件,数据被delete了,但是下次查询这件商品时,并不从数据库中查询,而是从缓存中查询,就会出现错误。

既然有了一级缓存,那么为什么要提供二级缓存呢?

二级缓存是mapper级别的缓存,多个sqlsession去操作同一个mapper的sql语句,多个sqlsession可以共用二级缓存,二级缓存是跨sqlsession的。二级缓存的作用范围更大。

还有一个原因,实际开发中,mybatis通常和spring进行整合开发。spring将事务放到service中管理,对于每一个service中的sqlsession是不同的,这是通过mybatis-spring中的org.mybatis.spring.mapper.mapperscannerconfigurer创建sqlsession自动注入到service中的。

每次查询之后都要进行关闭sqlsession,关闭之后数据被清空。所以spring整合之后,如果没有事务,一级缓存是没有意义的。

二级缓存

二级缓存原理:

深入理解MyBatis中的一级缓存与二级缓存

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

usermapper有一个二级缓存区域(按namespace分),其它mapper也有自己的二级缓存区域(按namespace分)。每一个namespace的mapper都有一个二级缓存区域,两个mapper的namespace如果相同,这两个mapper执行sql查询到数据将存在相同的二级缓存区域中。

开启二级缓存:

1,打开总开关

在mybatis的配置文件中加入:

<span style="font-size:18px;"><settings> 
 <!--开启二级缓存--> 
 <setting name="cacheenabled" value="true"/> 
</settings> </span> 

2,在需要开启二级缓存的mapper.xml中加入caceh标签

<span style="font-size:18px;"><cache/></span>

3,让使用二级缓存的pojo类实现serializable接口

<span style="font-size:18px;">public class user implements serializable {}</span> 

测试一下

<span style="font-size:18px;">@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(); 
}</span> 

输出结果:

<span style="font-size:18px;">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]</span> 

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

前面我们说到,spring和mybatis整合时, 每次查询之后都要进行关闭sqlsession,关闭之后数据被清空。所以spring整合之后,如果没有事务,一级缓存是没有意义的。那么如果开启二级缓存,关闭sqlsession后,会把该sqlsession一级缓存中的数据添加到namespace的二级缓存中。这样,缓存在sqlsession关闭之后依然存在。

总结:

对于查询多commit少且用户对查询结果实时性要求不高,此时采用mybatis二级缓存技术降低数据库访问量,提高访问速度。

但不能滥用二级缓存,二级缓存也有很多弊端,从mybatis默认二级缓存是关闭的就可以看出来。
二级缓存是建立在同一个namespace下的,如果对表的操作查询可能有多个namespace,那么得到的数据就是错误的。

举个简单的例子:

订单和订单详情,ordermapper、orderdetailmapper。在查询订单详情时我们需要把订单信息也查询出来,那么这个订单详情的信息被二级缓存在orderdetailmapper的namespace中,这个时候有人要修改订单的基本信息,那就是在ordermapper的namespace下修改,他是不会影响到orderdetailmapper的缓存的,那么你再次查找订单详情时,拿到的是缓存的数据,这个数据其实已经是过时的。

根据以上,想要使用二级缓存时需要想好两个问题:

     1)对该表的操作与查询都在同一个namespace下,其他的namespace如果有操作,就会发生数据的脏读。

     2)对关联表的查询,关联的所有表的操作都必须在同一个namespace。

好了,以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流,谢谢大家对的支持。

上一篇:

下一篇: