MyBatis | 一级缓存与二级缓存

一、什么是缓存?

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


一级缓存:是 SQlSession 级别的缓存。在操作数据库时需要构造 SqlSession 对象,在对象中有一个数据结构(HashMap)用于存储缓存数据。不同的 SqlSession 之间的缓存数据区域(HashMap)是互相不影响的。

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

二、具体介绍

1.使用一级缓存

我们先看一个使用缓存例子:这里我们是根据传入的 id 获取 Employee 对象的值,我们先使用同一个 SqlSession 对象,并且查询 id 相同的对象

@Test
public void testFirstCache() throws IOException {
    SqlSessionFactory sqlSessionFactory = Utils.getSqlSessionFactoty();
    SqlSession openSession = sqlSessionFactory.openSession();
    
    try {
        EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
        Employee employee = mapper.getEmployee(1);
        System.out.println(employee);
            
        Employee employee2 = mapper.getEmployee(1);
        System.out.println(employee2);
        System.out.println(employee==employee2);    
    } finally {
        openSession.close();
    }
    
}

他的运行结果是:

从结果中可以看出,第一次查询结束之后,就把 id 为 1 的数据放入到缓存中(本质上是放入 Map 对象中),第二次如果还是使用相同的 SqlSession 对象,则先会去一级缓存中找是否有 id 为 1 的员工的信息(Map 对象中是否有该对象),如果有,则直接从缓存中取出员工信息,如果没有,则会去数据库中查询相关信息

我们再来看一个一级缓存失效了的例子:假设这个时候我们查询的不是同一个 id,我们看看结果是怎样。在上面的代码中,我们将 Employee employee2 = mapper.getEmployee(1) 改为 Employee employee2 = mapper.getEmployee(2),即不是查询同一个 id,结果如下:

这也验证了我上面的结论

一级缓存失效的情况
  • SqlSession 对象不同, 导致每次使用的都是新的 SqlSession 对象
  • SqlSession 相同, 查询条件不同, 此时一级缓存中没有数据, 因为两次查询的内容不一样
  • SqlSession 相同, 两次查询直接执行了增删改查操作, 因为这次操作可能会对当前数据库有影响
  • SqlSession 相同, 手动清除了一级缓存(openSession.clearCache()

这些情况就不一一举例了,以后用到关注一下即可

2. 使用二级缓存

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


2.1 工作机制

  1. 一个会话, 查询一条数据, 这个数据就会被保存在当前会话的一次缓存(SqlSession)中
  2. 如果关闭会话, 那么一次缓存中的数据就会被保存到二级缓存(namespace)中,新的会话查询的内容, 就可以参照二级缓存
  3. 一个 xxxMapper 对应一个 namespace, 不同的 namespace 查出的数据会保存在自己对应的二级缓存中
  4. 查出的数据会先被保存在一级缓存中, 只有会话关闭或者提交之后, 以及缓存中的数据才会被转移到二级缓存

2.2 开启二级缓存步骤

  1. 开启全局二级缓存配置
  2. 去 mapper.xml 中配置使用二级缓存
  3. 我们的 POJO 需要实现序列化接口

在全局文件中开启二级缓存

<settings>
    <setting name="cacheEnabled" value="true"/>
</settings>

在 sql 映射文件中配置使用二级缓存,具体参数可以去官方文档查询

<mapper namespace="edu.just.mybatis.dao.EmployeeMapper">
    <cache eviction="FIFO" flushInterval="60000" readOnly="false" size="1024"></cache>
    ...
</mapper>

实现序列化

public class Employee implements Serializable{

    private static final long serialVersionUID = 1L;
    
    private Integer id;
    private String lastName;
    private String email;
    private Integer gender;
    private Department department;

        ...
}


2.3 二级缓存测试
这里我们通过两个不同的 SqlSession 对象创建两个 EmployeeMapper,这两个 EmployeeMapper 属于同一个 namespace

@Test
public void testSecondCache() throws IOException {
    SqlSessionFactory sqlSessionFactory = Utils.getSqlSessionFactoty();
    SqlSession openSession = sqlSessionFactory.openSession();
    SqlSession openSession2 = sqlSessionFactory.openSession();
        
    try {
        //1.创建两个不同的 EmployeeMapper 对象
        EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
        EmployeeMapper mapper2 = openSession2.getMapper(EmployeeMapper.class);
            
        Employee employee = mapper.getEmployee(1);
        openSession.close();          //关闭第第一个 SqlSession 会话
            
        //2.第二次查询是从二级缓存中拿到的, 并没有发送新的 sql
        //  此时是从 EmployeeMapper 的二级缓存中获取的数据
        Employee employee2 = mapper2.getEmployee(1);
        System.out.println(employee2);
        openSession.close();
        
    } finally {
        openSession.close();
    }
}

结果如下:

可以看到,虽然我们使用的是两个 SqlSession 对象,但是范围依旧是在 EmployeeMapper 这个 namespace 中,当我们关闭了第一个 SqlSession 对象后,此时第一次缓存中的数据被保存在了 EmployeeMapper 的二级缓存中,因此当我们重新查询相同的 id 的数据时,这个时候是在二级缓存中进行获取的

2.4 其他配置
①. cacheEnabled 设置的是二级缓存,一级缓存也一直可以使用
②. 每个 select 标签都有 useCache 标签,如果设置 useCache="false",那么一级缓存依旧使用,二级缓存则不会使用
③. 每个增删改查操作的 flushCache="true", 表示增删改查操作完成之后, 会自动清除缓存, 此时一级缓存和二级缓存都会被清空

三、参考

深入理解MyBatis中的一级缓存与二级缓存
【MyBatis学习13】MyBatis中的二级缓存

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 前言 主题是Mybatis一级和二级缓存的应用及源码分析。希望在本场chat结束后,能够帮助读者朋友明白以下三点。...
    余平的余_余平的平阅读 1,362评论 0 12
  • 参考互联网后端架构的文章 一级缓存 一级缓存是SqlSession级别的缓存。在操作数据库时需要构造sqlSess...
    wangxiaoda阅读 1,885评论 0 4
  • Tensorflow函数大全 操作函数 正则化 一 数据处理 v1 = [1.0,2.0,3.0,4.0] ...
    lolipops阅读 1,225评论 0 1
  • 凌晨三点半,因为作业没交睡不踏实,索性开灯起来写。 如果人生是本书,我已然在后半部。前半部完成于2015年,当看到...
    无尽蔓阅读 244评论 2 5
  • 16年如果里程碑一件是儿子出生,一件是加入培训师俱乐部,还有一件就是跳槽。 培训师俱乐部作为我的家,锻炼了演讲技巧...
    DennisFly阅读 406评论 0 0