MyBatis缓冲简介

缓存是存在内存中的临时数据,用户去查询数据就不用从磁盘上(关系型数据库数据文件)查询,从缓存中查询,从而提高查询效率,解决了高并发系统的性能问题。使用缓存可以减少和数据库的交互次数,减少系统开销,提高系统效率。当然,并不是所有数据都是适合使用缓存的,经常查询并且不经常改变的数据才适合使用缓存。MyBatis包含一个非常强大的查询缓存特性,它可以非常方便地定制和配置缓存,缓存可以极大的提升查询效率。MyBatis系统中默认定义了两级缓存:一级缓存和二级缓存默认情况下,只有一级缓存开启(SqlSession级别的缓存,也称为本地缓存);而二级缓存需要手动开启和配置,他是基于namespace级别的缓存。为了提高扩展性,MyBatis定义了缓存接口Cache,我们可以通过实现Cache接口来自定义二级缓存。

一级缓存

与数据库在同一次会话的数据会存放到一级缓存,如果在同一会话期间,可以直接从一级缓存中取到数据,而不再需要消耗资源从数据库中查,一级缓存的作用域是一个SqlSession,一级缓存也叫本地缓存。

  1. package com.wjh.dao;
  2. import com.wjh.po.User;
  3. import com.wjh.utils.MyBatisUtils;
  4. import org.apache.ibatis.session.SqlSession;
  5. /**
  6. * @author wjh
  7. * @date 2021/7/12 11:53 @Package com.wjh.dao
  8. */
  9. public class UserDaoTest {
  10. public static void main(String[] args) {
  11. SqlSession sqlSession = MyBatisUtils.getSqlSession();
  12. UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
  13. User user1 = userMapper.getUserById(1);
  14. User user2 = userMapper.getUserById(1);
  15. System.out.println(user1.toString());
  16. System.out.println(user2.toString());
  17. System.out.println(user1 == user2);
  18. sqlSession.close();
  19. }
  20. }

运行结果:
image.png
可以看到虽然查询id=1的用户了两次,但是却只执行了一次sql,也就是第二次查询没有连接数据库。

  1. package com.wjh.dao;
  2. import com.wjh.po.User;
  3. import com.wjh.utils.MyBatisUtils;
  4. import org.apache.ibatis.session.SqlSession;
  5. /**
  6. * @author wjh
  7. * @date 2021/7/12 11:53 @Package com.wjh.dao
  8. */
  9. public class UserDaoTest {
  10. public static void main(String[] args) {
  11. SqlSession sqlSession1 = MyBatisUtils.getSqlSession();
  12. UserMapper userMapper1 = sqlSession1.getMapper(UserMapper.class);
  13. User user1 = userMapper1.getUserById(1);
  14. System.out.println(user1.toString());
  15. sqlSession1.close();
  16. SqlSession sqlSession2 = MyBatisUtils.getSqlSession();
  17. UserMapper userMapper2 = sqlSession2.getMapper(UserMapper.class);
  18. User user2 = userMapper2.getUserById(1);
  19. System.out.println(user2.toString());
  20. sqlSession2.close();
  21. System.out.println(user1 == user2);
  22. }
  23. }

运行结果:
image.png
可以看到,在两次SqlSession中,即使都查询id=1的用户,但是执行了两次一样的sql,证明一级缓存的作用域是一个SqlSession。

一级缓存失效的情况:
(1)SqlSession不同,每个SqlSession是相互独立的。
(2)SqlSession相同但是查询条件不同,原因是当前缓存不存在这个数据。
(3)SqlSession相同,但是两次相同查询之间执行了增删改操作,增删改操作是会对当前数据产生影响的,所以会刷新缓存。
(4)使用SqlSession类的“clearCache()”方法来手动清除缓存。

二级缓存

二级也叫全局缓存,是基于namespace级别的缓存,一个namespace(即一个dao层接口),二级缓存的工作继承大致如下:
一个会话查询一条数据,这个数据就会被放在当前会话的一级缓存,如果当前会话关闭了,这个会话对应的一级缓存就没了。但是我们想要的是,会话关闭了,一级缓存中的数据被保存到二级缓存中,新的会话查询信息,就可以从二级缓存中获取内容,不同的mapper查出的数据会放在自己对应的缓存中。

使用步骤:
首先在核心配置文件中设置开启二级缓存:

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

然后,在需要开启缓存的xxxMapper.xml文件配置开启本接口的缓存:

  1. <cache/>
  1. <cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/>

两者都是可以开启二级缓存的,只是后者有了一些更加高级的配置:创建了一个 FIFO 缓存,每隔 60 秒刷新,最多可以存储结果对象或列表的 512 个引用,而且返回的对象被认为是只读的,因此对它们进行修改可能会在不同线程中的调用者产生冲突。

测试如下:

  1. package com.wjh.dao;
  2. import com.wjh.po.User;
  3. import com.wjh.utils.MyBatisUtils;
  4. import org.apache.ibatis.session.SqlSession;
  5. /**
  6. * @author wjh
  7. * @date 2021/7/12 11:53 @Package com.wjh.dao
  8. */
  9. public class UserDaoTest {
  10. public static void main(String[] args) {
  11. SqlSession sqlSession1 = MyBatisUtils.getSqlSession();
  12. UserMapper userMapper1 = sqlSession1.getMapper(UserMapper.class);
  13. User user1 = userMapper1.getUserById(1);
  14. System.out.println(user1.toString());
  15. sqlSession1.close();
  16. SqlSession sqlSession2 = MyBatisUtils.getSqlSession();
  17. UserMapper userMapper2 = sqlSession2.getMapper(UserMapper.class);
  18. User user2 = userMapper2.getUserById(1);
  19. System.out.println(user2.toString());
  20. sqlSession2.close();
  21. System.out.println(user1 == user2);
  22. }
  23. }

运行结果:
image.png
MyBatis缓存的原理图如下:
image.png

EhCache(了解即可)

Ehcache是一种广泛使用的java分布式缓存,用于通用缓存。

要在应用程序中使用Ehcache,首先需要引入依赖的jar包

<dependency> 
  <groupId>org.mybatis.caches</groupId> 
  <artifactId>mybatis-ehcache</artifactId>
  <version>1.1.0</version> 
</dependency>

其次,在xxxMapper.xml中配置即可:

<mapper namespace = “xxx.xxx.xxx” > 
  <cache type = “org.mybatis.caches.ehcache.EhcacheCache” /> 
</mapper>

然后,编写配置文件(基本是固定的了):

<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
         updateCheck="false">
    <diskStore path="./tmpdir/Tmp_EhCache"/>
    <defaultCache eternal="false" maxElementsInMemory="10000" overflowToDisk="false" diskPersistent="false"
                  timeToIdleSeconds="1800" timeToLiveSeconds="259200" memoryStoreEvictionPolicy="LRU"/>
    <cache name="cloud_user" eternal="false" maxElementsInMemory="5000" overflowToDisk="false" diskPersistent="false"
           timeToIdleSeconds="1800" timeToLiveSeconds="1800"
           memoryStoreEvictionPolicy="LRU"/> 
</ehcache>