Mybatis缓存
缓存是内存中的数据,常常来自数据库查询结果的保存,使用缓存,可以避免频繁的与数据库交互,进而提高响应速度。
mybatis也提供了缓存的支持,分为一级缓存和二级缓存
- ①一级缓存是sqlSession级别的缓存,在操作数据库时需要构建sqlSession对象,在对象中有一个数据结构(HashMap)用于存储缓存数据。不同的sqlSession之间的缓存数据区域是互相不影响的。
- ②二级缓存是mapper级别的缓存,多个sqlSession去操作同一个Mapper的sql语句,多个SqlSession可以共用二级缓存。二级缓存是跨SqlSession的。
一级缓存
①在同一个sqlSession,对数据库表user进行根据id分别进行两次查询,查看sql语句的执行情况
@Test
public void test1(){
// 根据SqlSessionFactory产生sqlsession
SqlSession sqlSession = sessionFactory.openSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
// 第一次进行查询,执行sql语句,将其结果放入缓存中
User u1 = userMapper.selectUserByUserId(1);
System.out.println(u1);
// 第二次查询,同一个sqlSession,会先在缓存中查找结果
// 如果有结果,则直接从缓存中读取,不进行数据库交互
User u2 = userMapper.selectUserByUserId(1);
System.out.println(u2);
sqlSession.close();
}
后台控制台打印情况
可以看到,第一次查询与数据库交互打印出sql语句,第二次并未与数据库交互打印sql语句
②同样是对user表进行两次查询操作,在两次查询中间进行一次update操作
@Test
public void test1(){
// 根据SqlSessionFactory产生sqlsession
SqlSession sqlSession = sessionFactory.openSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
// 第一次进行查询,执行sql语句,将其结果放入缓存中
User u1 = userMapper.selectUserByUserId(1);
System.out.println(u1);
// 进行更新操作
u1.setSex("女");
userMapper.updateUserByUserId(u1);
sqlSession.commit();
// 第二次查询,同一个sqlSession,会先在缓存中查找结果
// 如果有结果,则直接从缓存中读取,不进行数据库交互
User u2 = userMapper.selectUserByUserId(1);
System.out.println(u2);
sqlSession.close();
}
控制台打印sql
③总结
1、第一次查询用户id为1的用户,先去缓存中寻找是否有id为1的用户,如果没有,从数据库中查询用户信息。得到用户信息,将用户信息存放在缓存中。
2、如果中间执行了commit操作(新增,更新,删除),则会清空sqlSession中的一级缓存,目的使缓存中的数据是最新数据,避免脏读。
3、第二次发起的查询,先去缓存中寻找id为1的用户信息,缓存中有,则直接从缓存中获取用户信息。
二级缓存
二级缓存的原理和一级缓存一样,第一次查询,会将数据放入缓存中,第二次查询直接去缓存中取。但是一级缓存是基于sqlSession的,二级缓存是基于mapper文件的namespace的,也就是说多个sqlSession是可以共享一个mapper中的二级缓存区域,并且如果两个mapper的namespace相同,即使两个mapper,那么这两个mapper中执行的sql查询到的数据也将相同的二级缓存区域中。
①开启二级缓存
和一级缓存不一样,二级缓存需要手动开启
首先在全局配置文件sqlMapConfig.xml文件中加入如下的代码:
<!-- 开启二级缓存 -->
<settings>
<setting name="cacheEnabled" value="true"/>
</settings>
其次在UserMapper.xml文件中开启缓存
<!-- 开启二级缓存 -->
<cache></cache>
在mapper.xml中就这么一个空标签,其实这里可以配置的。
在PerpetualCache这个类是mybatis默认实现缓存功能,也可以去实现Cache接口来自定义缓存的。
!
上图中我们可以看到二级缓存中,底层还是HashMap结构
②po类实现serializable序列化接口
开启二级缓存后,需要将缓存的pojo类实现serializable序列化接口,为了将缓存数据取出执行反序列化操作,因为二级缓存数据存储介质多样化,不一定只存在内存中,可能在硬盘中存放,如果我们需要再取这个缓存的话就需要反序列化。所以mybatis中的pojo类都去实现了serializable接口
③useCache和flushCache
mybatis中可以配置useCache和flushCache配置项,useCache用来是否禁用二级缓存的 ,在statement中设置useCache=false可以禁用当前select语句的二级缓存,即每次查询都是sql去查询,默认情况下是true,即该sql使用二级缓存
<select id="selectUserByUserId" useCache="false"
resultType="com.lagou.pojo.User" parameterType="int">
select * from user where id=#{id}
</select>
每次都需要查询最新的数据,设置为useCache=false,禁用二级缓存,直接从数据库中获取 。
在mapper的同一个namespace,如果有其他的insert、update、delete操作数据后需要刷新缓存,如果不刷新缓存容易产生脏读。
设置statement配置中的flushCache=true属性,默认情况下是true,即刷新缓存,false则不会刷新。使用缓存时如果修改数据库表中的查询数据容易产生脏读。
<select id="selectUserByUserId" flushCache="true" useCache="false"
resultType="com.lagou.pojo.User" parameterType="int">
select * from user where id=#{id}
</select>
一般情况下执行commit操作都需要刷新缓存,flushCache=true表示刷新缓存,这样可以避免数据脏读,所以我们不需要设置,默认即可。
二级缓存整合redis
mybatis自带二级缓存是个单服务器工作,无法实现分布式缓存。什么是分布式缓存?假如:现在有2个服务器A和B,用户访问的时候访问了A服务器,查询的缓存就放在A服务器上,假设现在有个用户访问的是服务器B,那么在B服务器上无法获取到A服务器上的缓存
为了解决这个问题,就产生了分布式缓存的概念,专门用来存放缓存数据结构的,这样不同的服务器要缓存数据都往它那里存,取缓存数据也从那里取
上图所示,使用第三方框架,将缓存都存放在这个第三方框架中,无论多少台服务器,我们都能从缓存服务器中获取数据。