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中的二级缓存

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,723评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,485评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,998评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,323评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,355评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,079评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,389评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,019评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,519评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,971评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,100评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,738评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,293评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,289评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,517评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,547评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,834评论 2 345

推荐阅读更多精彩内容

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