二级缓存介绍

在上文中提到的一级缓存中,其最大的共享范围就是一个SqlSession内部,如果多个SqlSession之间需要共享缓存,则需要使用到二级缓存。开启二级缓存后,会使用CachingExecutor装饰Executor,进入一级缓存的查询流程前,先在CachingExecutor进行二级缓存的查询,具体的工作流程如下所示。

二级缓存开启后,同一个namespace下的所有操作语句,都影响着同一个Cache,即二级缓存被多个SqlSession共享,是一个全局的变量。

当开启缓存后,数据的查询执行的流程就是 二级缓存 -> 一级缓存 -> 数据库。

二级缓存配置

要正确的使用二级缓存,需完成如下配置的。

首先,在MyBatis的配置文件中开启二级缓存。

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

然后,在MyBatis的 Mapper XML 中加入 cache 或者 cache-ref 标签。

cache标签用于声明这个namespace需要使用二级缓存,并且可以自定义配置。

<cache/>
  • type:cache使用的类型,默认是PerpetualCache,这在一级缓存中提到过。
  • eviction: 定义回收的策略,常见的有FIFO,LRU。
  • flushInterval: 配置一定时间自动刷新缓存,单位是毫秒。
  • size: 最多缓存对象的个数。
  • readOnly: 是否只读,若配置可读写,则需要对应的实体类能够序列化。
  • blocking: 若缓存中找不到对应的key,是否会一直blocking,直到有对应的数据进入缓存。

cache-ref代表引用别的命名空间的Cache配置,两个命名空间的操作使用的是同一个Cache。

<cache-ref namespace="mapper.StudentMapper"/>

二级缓存实验

public void testCacheWithoutCommitOrClose() throws Exception {
SqlSession sqlSession1 = factory.openSession(false); // 不自动提交事务
SqlSession sqlSession2 = factory.openSession(false); // 不自动提交事务 StudentMapper studentMapper = sqlSession1.getMapper(StudentMapper.class);
StudentMapper studentMapper2 = sqlSession2.getMapper(StudentMapper.class); System.out.println(studentMapper.getStudentById(1)); // 查询数据库
System.out.println(studentMapper2.getStudentById(1)); // 查询数据库,二级缓存不起作用
}
public void testCacheWithCommitOrClose() throws Exception {
SqlSession sqlSession1 = factory.openSession(false); // 不自动提交事务
SqlSession sqlSession2 = factory.openSession(false); // 不自动提交事务 StudentMapper studentMapper = sqlSession1.getMapper(StudentMapper.class);
StudentMapper studentMapper2 = sqlSession2.getMapper(StudentMapper.class); System.out.println(studentMapper.getStudentById(1)); // 查询数据库
sqlSession1.commit(); // 提交事务
System.out.println(studentMapper2.getStudentById(1)); // 查询缓存,二级缓存起作用
}
public void testCacheWithUpdate() throws Exception {
SqlSession sqlSession1 = factory.openSession(false); // 不自动提交事务
SqlSession sqlSession2 = factory.openSession(false); // 不自动提交事务
SqlSession sqlSession3 = factory.openSession(false); // 不自动提交事务 StudentMapper studentMapper = sqlSession1.getMapper(StudentMapper.class);
StudentMapper studentMapper2 = sqlSession2.getMapper(StudentMapper.class);
StudentMapper studentMapper3 = sqlSession3.getMapper(StudentMapper.class); System.out.println(studentMapper.getStudentById(1)); // 查询数据库
sqlSession1.commit(); // 提交事务
System.out.println(studentMapper2.getStudentById(1)); // 查询缓存,二级缓存起作用 studentMapper3.updateStudentName("方方",1); // 更新数据
sqlSession3.commit(); // 提交事务
System.out.println(studentMapper2.getStudentById(1)); // 查询数据库,说明清除了二级缓存
}
public void testCacheWithDiffererntNamespace() throws Exception {
SqlSession sqlSession1 = factory.openSession(false); // 不自动提交事务
SqlSession sqlSession2 = factory.openSession(false); // 不自动提交事务
SqlSession sqlSession3 = factory.openSession(false); // 不自动提交事务 StudentMapper studentMapper = sqlSession1.getMapper(StudentMapper.class);
StudentMapper studentMapper2 = sqlSession2.getMapper(StudentMapper.class);
ClassMapper classMapper = sqlSession3.getMapper(ClassMapper.class); System.out.println(studentMapper.getStudentByIdWithClassInfo(1)); // 关联查询数据库
sqlSession1.close();
System.out.println(studentMapper2.getStudentByIdWithClassInfo(1)); // 查询缓存,二级缓存起作用 classMapper.updateClassName("特色一班",1); // 更新其中一个表的数据
sqlSession3.commit(); // 提交更新事务
System.out.println(studentMapper2.getStudentByIdWithClassInfo(1)); // 仍然查询缓存,引起了脏读
}
// 让ClassMapper引用StudenMapper的命名空间,这样两个映射文件对应的SQL操作都会使用同一块缓存
public void testCacheWithDiffererntNamespace() throws Exception {
SqlSession sqlSession1 = factory.openSession(false); // 不自动提交事务
SqlSession sqlSession2 = factory.openSession(false); // 不自动提交事务
SqlSession sqlSession3 = factory.openSession(false); // 不自动提交事务 StudentMapper studentMapper = sqlSession1.getMapper(StudentMapper.class);
StudentMapper studentMapper2 = sqlSession2.getMapper(StudentMapper.class);
ClassMapper classMapper = sqlSession3.getMapper(ClassMapper.class); System.out.println(studentMapper.getStudentByIdWithClassInfo(1)); // 关联查询数据库
sqlSession1.close();
System.out.println(studentMapper2.getStudentByIdWithClassInfo(1)); // 查询缓存,二级缓存起作用 classMapper.updateClassName("特色一班",1); // 更新其中一个表的数据
sqlSession3.commit(); // 提交更新事务
System.out.println(studentMapper2.getStudentByIdWithClassInfo(1)); // 关联查询数据库,二级缓存失效
}

总结

  1. MyBatis的二级缓存相对于一级缓存来说,实现了SqlSession之间缓存数据的共享,同时粒度更加的细,能够到namespace级别,通过Cache接口实现类不同的组合,对Cache的可控性也更强。
  2. MyBatis在多表查询时,极大可能会出现脏数据,有设计上的缺陷,安全使用二级缓存的条件比较苛刻。
  3. 在分布式环境下,由于默认的MyBatis Cache实现都是基于本地的,分布式环境下必然会出现读取到脏数据,需要使用集中式缓存将MyBatis的Cache接口实现,有一定的开发成本,直接使用Redis、Memcached等分布式缓存可能成本更低,安全性也更高

参考:https://mybatis.org/mybatis-3/zh/configuration.html#settings

http://mybatis.org/spring/zh/factorybean.html

https://tech.meituan.com/2018/01/19/mybatis-cache.html

https://blog.csdn.net/u010349169/article/details/41408341

https://blog.csdn.net/mengsofts/article/details/88074790

MyBatis 二级缓存实现详解及使用注意事项的更多相关文章

  1. MyBatis 二级缓存全详解

    目录 MyBatis 二级缓存介绍 二级缓存开启条件 探究二级缓存 二级缓存失效的条件 第一次SqlSession 未提交 更新对二级缓存影响 探究多表操作对二级缓存的影响 二级缓存源码解析 二级缓存 ...

  2. MyBatis 一级缓存实现详解及使用注意事项

    一级缓存介绍 在应用运行过程中,我们有可能在一次数据库会话中,执行多次查询条件完全相同的SQL,MyBatis提供了一级缓存的方案优化这部分场景,如果是相同的SQL语句,会优先命中一级缓存,避免直接对 ...

  3. MyBatis 一级缓存、二级缓存全详解(一)

    目录 MyBatis 一级缓存.二级缓存全详解(一) 什么是缓存 什么是MyBatis中的缓存 MyBatis 中的一级缓存 初探一级缓存 探究一级缓存是如何失效的 一级缓存原理探究 还有其他要补充的 ...

  4. 《深入理解mybatis原理6》 MyBatis的一级缓存实现详解 及使用注意事项

    <深入理解mybatis原理> MyBatis的一级缓存实现详解 及使用注意事项 0.写在前面   MyBatis是一个简单,小巧但功能非常强大的ORM开源框架,它的功能强大也体现在它的缓 ...

  5. 《深入理解mybatis原理》 MyBatis的一级缓存实现详解 及使用注意事项

    MyBatis是一个简单,小巧但功能非常强大的ORM开源框架,它的功能强大也体现在它的缓存机制上.MyBatis提供了一级缓存.二级缓存 这两个缓存机制,能够很好地处理和维护缓存,以提高系统的性能.本 ...

  6. mybatis深入理解(五)-----MyBatis的一级缓存实现详解 及使用注意事项

    0.写在前面 MyBatis是一个简单,小巧但功能非常强大的ORM开源框架,它的功能强大也体现在它的缓存机制上.MyBatis提供了一级缓存.二级缓存 这两个缓存机制,能够很好地处理和维护缓存,以提高 ...

  7. Hibernate中一级缓存和二级缓存使用详解

    一.一级缓存二级缓存的概念解释 (1)一级缓存就是Session级别的缓存,一个Session做了一个查询操作,它会把这个操作的结果放在一级缓存中,如果短时间内这个 session(一定要同一个ses ...

  8. Spring 整合 Hibernate 时启用二级缓存实例详解

    写在前面: 1. 本例使用 Hibernate3 + Spring3: 2. 本例的查询使用了 HibernateTemplate: 1. 导入 ehcache-x.x.x.jar 包: 2. 在 a ...

  9. Mybatis SQL映射文件详解

    Mybatis SQL映射文件详解 mybatis除了有全局配置文件,还有映射文件,在映射文件中可以编写以下的顶级元素标签: cache – 该命名空间的缓存配置. cache-ref – 引用其它命 ...

随机推荐

  1. Java Jar包压缩、解压使用

    什么是jar包JAR(Java Archive)是Java的归档文件,它是一种与平台无关的文件格式,它允许将许多文件组合成一个压缩文件. 如何打/解包使用jdk/bin/jar.exe工具,配置完环境 ...

  2. alert之后才执行

    如果在正常情况下,代码要在alert之后才执行,解决办法:将要执行的代码用setTimeout延迟执行即可(原因:页面未加载完毕)

  3. AOP中环绕通知的书写和配置

    package com.hope.utils;import org.aspectj.lang.ProceedingJoinPoint;import org.aspectj.lang.annotatio ...

  4. 【MySQL】排名函数

    https://www.cnblogs.com/shizhijie/p/9366247.html 排名函数 主要有rank和dense_rank两种 区别: rank在排名的时候,排名的键一样的时候是 ...

  5. Python matplotlib绘图设置图例

    一.语法简介 plt.legend(loc=2,edgecolor='red',facecolor='green',shadow='True',fontsize=10) #edgecolor 图例边框 ...

  6. 09 - Vue3 UI Framework - Table 组件

    接下来做个自定义的表格组件,即 table 组件 返回阅读列表点击 这里 需求分析 开始之前我们先做一个简单的需求分析 基于原生 table 标签的强语义 允许用户自定义表头.表体 可选是否具有边框 ...

  7. [BUUCTF]PWN——[BJDCTF 2nd]secret

    [BJDCTF 2nd]secret 附件 步骤: 例行检查,64位程序,开启了canary和nx 本地试运行一下,看看程序大概的情况,好像是一个什么游戏 64位ida载入,检索程序里的字符串,发现了 ...

  8. springboot学习(一)

    最近想学习springboot所以在网上找了很多文章参考怎么构建springboot项目以及集成mybatis 集成mybatis的部分参考了这两篇文章 https://blog.csdn.net/t ...

  9. .NET静态代码织入——肉夹馍(Rougamo)

    肉夹馍是什么 肉夹馍通过静态代码织入方式实现AOP的组件..NET常用的AOP有Castle DynamicProxy.AspectCore等,以上两种AOP组件都是通过运行时生成一个代理类执行AOP ...

  10. CF19A World Football Cup 题解

    Content 有 \(n\) 个球队参加一场足球比赛,比赛排名前 \(\dfrac{n}{2}\) 的队伍将会进入下一轮的淘汰赛.比赛将会打 \(\dfrac{n(n-1)}{2}\) 场,胜者得 ...