MyBatis 的一级缓存和二级缓存

一级缓存

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

一级缓存的工作原理:

用户发起查询请求,查找某条数据,sqlSession 先去缓存中查找,是否有该数据,如果有,读取;
如果没有,从数据库中查询,并将查询到的数据放入一级缓存区域,供下次查找使用。
但 sqlSession 执行commit,即增删改操作时会清空缓存。这么做的目的是避免脏读。

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

那么为什么要提供二级缓存呢?

  1. 二级缓存是 mapper 级别的缓存,多个SqlSession去操作同一个Mapper的sql语句,多个
    SqlSession可以共用二级缓存,二级缓存是跨SqlSession的。二级缓存的作用范围更大。
  2. 实际开发中,MyBatis 通常和 Spring 进行整合开发。每次查询之后都要进行关闭 sqlSession ,关闭之后数据被清空。所以 spring 整合之后,如果没有开启事务,一级缓存 是没有意义的。

二级缓存

二级缓存原理:

二级缓存是 mapper 级别的缓存,多个 SqlSession 去操作同一个Mapper的sql语句,多个 SqlSession
可以共用二级缓存,二级缓存是跨 SqlSession 的。
UserMapper有一个二级缓存区域(按 namespace 划分),每个 mapper 也有自己的二级缓存区域
(按namespace分)。
每一个 namespace 的 mapper 都有一个二级缓存区域,如果相同两个 mapper 的 namespace ,这两
个mapper执行sql查询到数据将存在相同的二级缓存区域中。

开启二级缓存

1,打开总开关
在MyBatis的全局配置文件中加入:

<settings> 
	<!-- 开启二级缓存 --> 
	<setting name="cacheEnabled" value="true"/> 
</settings>

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

<cache/>

3,让使用二级缓存的 POJO 类实现 Serializable 接口

public class User implements Serializable {.... }

测试:

@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(); 
}

输出结果:

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

总结:

对于查询多、commit少且用户对查询结果实时性要求不高,此时采用 mybatis 二级缓存技术降低数据
库访问量,提高访问速度。
但不能滥用二级缓存,二级缓存也有很多弊端,从MyBatis默认二级缓存是关闭的就可以看出来。
二级缓存是建立在同一个 namespace下的,如果对表的操作查询可能有多个 namespace,那么得到的
数据就是错误的。

例如:
订单 和 订单详情 分别是 orderMapper、orderDetailMapper。

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

二级缓存的使用原则

1. 只能在一个命名空间下使用二级缓存
由于二级缓存中的数据是基于namespace的,即不同 namespace 中的数据互不干扰。
在多个namespace中存在对同一个表的操作,那么这多个namespace中的数据可能就会出现不一
致现象。
2. 在单表上使用二级缓存
如果一个表与其它表有关联关系,那么就非常有可能存在多个 namespace 对同一数据的操作。
而不同 namespace 中的数据相互干扰,所以就有可能出现多个 namespace 中的数据不一致现
象。
3. 查询多于修改时使用二级缓存
在查询操作远远多于增删改操作的情况下可以使用二级缓存。因为任何增删改操作都将刷新二级缓
存,对二级缓存的频繁刷新将降低系统性

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值