MyBatis缓存机制
Posted 曾吹雨
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MyBatis缓存机制相关的知识,希望对你有一定的参考价值。
缓存Cache
一、简介
1、缓存机制介绍
当客户端发起一次查询请求时,首先通过java程序进行网络传输访问mysql数据库及对应的数据的服务器硬盘,当第二次的请求也是查询相同的数据时再通过这个流程显然有点“浪费”上次请求访问到的资源,所以我们将第一次查询到的数据存到缓存区域,当发生下一次相同请求时直接在缓存区域拿就行了。
2. 一级缓存和二级缓存
①使用顺序
查询的顺序是:
- 先查询二级缓存,因为二级缓存中可能会有其他程序已经查出来的数据,可以拿来直接使用。
- 如果二级缓存没有命中,再查询一级缓存
- 如果一级缓存也没有命中,则查询数据库
- SqlSession关闭之前,一级缓存中的数据会写入二级缓存
②效用范围
- 一级缓存:SqlSession级别
- 二级缓存:SqlSessionFactory级别
它们之间范围的大小参考下面图:
二、一级缓存
当使用相同查询条件查询数据时,一共只打印了一条SQL语句,两个变量指向同一个对象。
一级缓存失效的情况:
- 不是同一个SqlSession
- 同一个SqlSession但是查询条件发生了变化
- 同一个SqlSession两次查询期间执行了任何一次增删改操作
- 同一个SqlSession两次查询期间手动清空了缓存
- 同一个SqlSession两次查询期间提交了事务
三、二级缓存
3.1 mybatis自带的二级缓存
3.1.1 代码测试二级缓存
① 开启二级缓存功能
在想要使用二级缓存的Mapper配置文件中加入cache标签
<mapper namespace="com.zengchuiyu.mybatis.dao.EmployeeMapper">
<!-- 启动二级缓存功能 -->
<cache/>
②让实体类支持序列化
public class Employee implements Serializable {
③junit测试
这个功能的测试操作需要将SqlSessionFactory对象设置为成员变量
public class CacheTest {
private SqlSessionFactory factory;
@Before
public void init() throws IOException {
factory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
}
//测试二级缓存,(mybatis自带的)
@Test
public void test1(){
SqlSession session = factory.openSession();
EmployeeMapper mapper = session.getMapper(EmployeeMapper.class);
Employee employee = mapper.selectEmpById(2);
System.out.println("employee = " + employee);
//在执行第二次查询前,关闭当前SqlSession
session.close();
//开启新的SqlSession
session = factory.openSession();
mapper = session.getMapper(EmployeeMapper.class);
employee = mapper.selectEmpById(2);
System.out.println("employee = " + employee);
session.close();
}
}
打印效果:
22:48:18.669 [main] DEBUG com.zengchuiyu.mybatis.dao.EmployeeMapper - Cache Hit Ratio [com.zengchuiyu.mybatis.dao.EmployeeMapper]: 0.5
④缓存命中率
日志中打印的Cache Hit Ratio叫做缓存命中率
Cache Hit Ratio [com.atguigu.mybatis.EmployeeMapper]: 0.0(0/1)
Cache Hit Ratio [com.atguigu.mybatis.EmployeeMapper]: 0.5(1/2)
Cache Hit Ratio [com.atguigu.mybatis.EmployeeMapper]: 0.6666666666666666(2/3)
Cache Hit Ratio [com.atguigu.mybatis.EmployeeMapper]: 0.75(3/4)
Cache Hit Ratio [com.atguigu.mybatis.EmployeeMapper]: 0.8(4/5)
缓存命中率=命中缓存的次数/查询的总次数
3.1.2 查询结果存入二级缓存的时机
结论:SqlSession关闭的时候,一级缓存中的内容会被存入二级缓存
3.1.3 二级缓存相关配置
- eviction属性:缓存回收策略
LRU(Least Recently Used) – 最近最少使用的:移除最长时间不被使用的对象。
FIFO(First in First out) – 先进先出:按对象进入缓存的顺序来移除它们。
SOFT – 软引用:移除基于垃圾回收器状态和软引用规则的对象。
WEAK – 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。
默认的是 LRU。
- flushInterval属性:刷新间隔,单位毫秒
默认情况是不设置,也就是没有刷新间隔,缓存仅仅调用语句时刷新
- size属性:引用数目,正整数
代表缓存最多可以存储多少个对象,太大容易导致内存溢出
- readOnly属性:只读,true/false
true:只读缓存;会给所有调用者返回缓存对象的相同实例。因此这些对象不能被修改。这提供了很重要的性能优势。
false:读写缓存;会返回缓存对象的拷贝(通过序列化)。这会慢一些,但是安全,因此默认是 false。
四、整合EHCache
4.1 EHCache简介
Ehcache is an open source, standards-based cache that boosts performance, offloads your database, and simplifies scalability. It’s the most widely-used Java-based cache because it’s robust, proven, full-featured, and integrates with other popular libraries and frameworks. Ehcache scales from in-process caching, all the way to mixed in-process/out-of-process deployments with terabyte-sized caches.
Ehcache是一个开源的,基于标准的缓存,可以提高性能,卸载数据库,简化可伸缩性。它是最广泛使用的基于java的缓存,因为它健壮、可靠、功能齐全,并与其他流行的库和框架集成。Ehcache从进程内缓存扩展到具有tb大小缓存的进程内/进程外混合部署。
4.2 整合操作
①Mybatis环境
在Mybatis环境下整合EHCache,前提当然是要先准备好Mybatis的环境。
②添加依赖
- 依赖信息:
<!-- Mybatis EHCache整合包 -->
<dependency>
<groupId>org.mybatis.caches</groupId>
<artifactId>mybatis-ehcache</artifactId>
<version>1.2.1</version>
</dependency>
<!-- slf4j日志门面的一个具体实现 -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
- 依赖传递情况:
- 各主要jar包作用
jar包名称 | 作用 |
---|---|
mybatis-ehcache | Mybatis和EHCache的整合包 |
ehcache | EHCache核心包 |
slf4j-api | SLF4J日志门面包 |
logback-classic | 支持SLF4J门面接口的一个具体实现 |
③整合EHCache
[1]创建EHCache配置文件
ehcache.xml
[2]文件内容
<?xml version="1.0" encoding="utf-8" ?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">
<!-- 磁盘保存路径 -->
<diskStore path="D:\\zengchuiyu\\ehcache"/>
<defaultCache
maxElementsInMemory="1000"
maxElementsOnDisk="10000000"
eternal="false"
overflowToDisk="true"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU">
</defaultCache>
</ehcache>
引入第三方框架或工具时,配置文件的文件名可以自定义吗?
可以自定义:文件名是由我告诉其他环境
不能自定义:文件名是框架内置的、约定好的,就不能自定义,以避免框架无法加载这个文件
④加入logback日志
存在SLF4J时,作为简易日志的log4j将失效,此时我们需要借助SLF4J的具体实现logback来打印日志。
[1]各种Java日志框架简介
门面:
名称 | 说明 |
---|---|
JCL(Jakarta Commons Logging) | 陈旧 |
SLF4J(Simple Logging Facade for Java)★ | 适合 |
jboss-logging | 特殊专业领域使用 |
实现:
名称 | 说明 |
---|---|
log4j★ | 最初版 |
JUL(java.util.logging) | JDK自带 |
log4j2 | Apache收购log4j后全面重构,内部实现和log4j完全不同 |
logback★ | 优雅、强大 |
注:标记★的技术是同一作者。
[2]logback配置文件
<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="true">
<!-- 指定日志输出的位置 -->
<appender name="STDOUT"
class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<!-- 日志输出的格式 -->
<!-- 按照顺序分别是:时间、日志级别、线程名称、打印日志的类、日志主体内容、换行 -->
<pattern>[%d{HH:mm:ss.SSS}] [%-5level] [%thread] [%logger] [%msg]%n</pattern>
</encoder>
</appender>
<!-- 设置全局日志级别。日志级别按顺序分别是:DEBUG、INFO、WARN、ERROR -->
<!-- 指定任何一个日志级别都只打印当前级别和后面级别的日志。 -->
<root level="DEBUG">
<!-- 指定打印日志的appender,这里通过“STDOUT”引用了前面配置的appender -->
<appender-ref ref="STDOUT" />
</root>
<!-- 根据特殊需求指定局部日志级别 -->
<logger name="com.atguigu.crowd.mapper" level="DEBUG"/>
</configuration>
⑤ EHCache配置文件说明
当借助CacheManager.add(“缓存名称”)创建Cache时,EhCache便会采用指定的的管理策略。
defaultCache标签各属性说明:
五、缓存基本原理
5.1 Cache接口
① 顶级接口
org.apache.ibatis.cache.Cache接口:所有缓存都必须实现的顶级接口
② Cache接口中的方法
③ 缓存的本质
根据Cache接口中方法的声明我们能够看到,缓存的本质是一个Map。
5.2 PerpetualCache
org.apache.ibatis.cache.impl.PerpetualCache是Mybatis的默认缓存,也是Cache接口的默认实现。Mybatis一级缓存和自带的二级缓存都是通过PerpetualCache来操作缓存数据的。但是这就奇怪了,同样是PerpetualCache这个类,怎么能区分出来两种不同级别的缓存呢?
其实很简单,调用者不同。
一级缓存:由BaseExecutor调用PerpetualCache
二级缓存:由CachingExecutor调用PerpetualCache,而CachingExecutor可以看做是对BaseExecutor的装饰
以上是关于MyBatis缓存机制的主要内容,如果未能解决你的问题,请参考以下文章