mybatis缓存-二级缓存
1.2 二级缓存
【官方声明】 => 如何开启【二级缓存】
默认情况下,只启用了本地的会话缓存,它仅仅对一个会话中的数据进行缓存。 要启用全局的二级缓存,只需要在你的 SQL 映射文件中添加一行:
- 在XML映射文件中添加以下代码,以开启【二级缓存】
<cache/>
【官方声明】 => 【二级缓存】的作用
- 映射语句文件中的所有 select 语句的结果将会被缓存。
- 映射语句文件中的所有 insert、update 和 delete 语句会刷新缓存。
- 缓存会使用最近最少使用算法(LRU, Least Recently Used)算法来清除不需要的缓存。
- 缓存不会定时进行刷新(也就是说,没有刷新间隔)。
- 缓存会保存列表或对象(无论查询方法返回哪种)的 1024 个引用。
- 缓存会被视为读/写缓存,这意味着获取到的对象并不是共享的,可以安全地被调用者修改,而不干扰其他调用者或线程所做的潜在修改。
【官方提示】 => 【二级缓存】的作用域
- 缓存只作用于 cache 标签所在的映射文件中的语句。如果你混合使用 Java API 和 XML 映射文件,在共用接口中的语句将不会被默认缓存。你需要使用 @CacheNamespaceRef 注解指定缓存作用域。
【官方声明】 => <cache>标签的属性修改
<cache
eviction="FIFO"
flushInterval="60000"
size="512"
readOnly="true"/>
这个更高级的配置创建了一个 FIFO 缓存,每隔 60 秒刷新,最多可以存储结果对象或列表的 512 个引用,而且返回的对象被认为是只读的,因此对它们进行修改可能会在不同线程中的调用者产生冲突。
可用的清除策略有:
LRU– 最近最少使用:移除最长时间不被使用的对象。FIFO– 先进先出:按对象进入缓存的顺序来移除它们。SOFT– 软引用:基于垃圾回收器状态和软引用规则移除对象。WEAK– 弱引用:更积极地基于垃圾收集器状态和弱引用规则移除对象。默认的清除策略是 LRU。
flushInterval(刷新间隔)属性可以被设置为任意的正整数,设置的值应该是一个以毫秒为单位的合理时间量。 默认情况是不设置,也就是没有刷新间隔,缓存仅仅会在调用语句时刷新。
size(引用数目)属性可以被设置为任意正整数,要注意欲缓存对象的大小和运行环境中可用的内存资源。默认值是 1024。
readOnly(只读)属性可以被设置为 true 或 false。只读的缓存会给所有调用者返回缓存对象的相同实例。 因此这些对象不能被修改。这就提供了可观的性能提升。而可读写的缓存会(通过序列化)返回缓存对象的拷贝。 速度上会慢一些,但是更安全,因此默认值是 false。
- 总结

1.2.1 什么是二级缓存?
MyBatis的二级缓存是Application级别的缓存,它可以提高对数据库查询的效率,以提高应用的性能。
二级缓存即使当一级缓存被清除/关闭也会存在(即sqlsession.close()方法执行后依旧会保存查询缓存)
SqlSessionFactory层面上的二级缓存默认是不开启的,二级缓存的开启需要进行配置,实现二级缓存的时候,MyBatis要求返回的POJO必须是可序列化的( 要求实现Serializable接口)
1.2.2 二级缓存的作用
- 映射语句文件中的所有select语句将会被缓存。
- 映射语句文件中的所有insert、update和delete语句会刷新缓存。
- 缓存会使用默认的Least Recently Used(LRU,最近最少使用的)算法来收回。
- 根据时间表,比如No Flush Interval,(CNFI没有刷新间隔),缓存不会以任何时间顺序来刷新。
- 缓存会存储列表集合或对象(无论查询方法返回什么)的1024个引用
- 缓存会被视为是read/write(可读/可写)的缓存,意味着对象检索不是共享的,而且可以安全的被调用者修改,不干扰其他调用者或线程所做的潜在修改。
1.2.3 测试
- 实现类 => 【实现Serializable接口】
@Data
//实现二级缓存返回的pojo对象必须要求是安全的。
//由于二级缓存的数据不一定都是存储到内存中,它的存储介质多种多样,所以需要给缓存的对象执行序列化。如果存储在内存中的话,实测不序列化也可以的。
public class User implements Serializable {
private int id;
private String name;
private String pwd;
}
XML配置文件 => 【开启二级缓存】
- 默认配置
<cache/>
- 自定义配置
<cache
eviction="FIFO"
flushInterval="3000"
size="512"
readOnly="true"/>
- 测试操作
@Test
public void Test03(){
SqlSession sqlSession01 = MybatisUtils.getSqlSession();
SqlSession sqlSession02 = MybatisUtils.getSqlSession();
UserMapper mapper01 = sqlSession01.getMapper(UserMapper.class);
User user01 = mapper01.queryUserById(2);
System.out.println(user01.toString());
sqlSession01.close();
System.out.println("------------***************----------------");
UserMapper mapper02 = sqlSession02.getMapper(UserMapper.class);
User user02 = mapper02.queryUserById(2);
System.out.println(user02.toString());
System.out.println(user01==user02);
sqlSession02.close();
}
第一次先不开启二级缓存

- 运行结果如下:可以看出再不开启二级缓存的情况下,两个sqlsession产生的结果对象并不一致,且查询操作sql语句使用了两次
Logging initialized using 'class org.apache.ibatis.logging.stdout.StdOutImpl' adapter.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
Opening JDBC Connection
Created connection 2130772866.
==> Preparing: select * from mybatis.user where id=?
==> Parameters: 2(Integer)
<== Columns: id, name, pwd
<== Row: 2, lisi, 123456
<== Total: 1
User(id=2, name=lisi, pwd=123456)
Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@7f010382]
Returned connection 2130772866 to pool.
------------***************----------------
Opening JDBC Connection
Checked out connection 2130772866 from pool.
==> Preparing: select * from mybatis.user where id=?
==> Parameters: 2(Integer)
<== Columns: id, name, pwd
<== Row: 2, lisi, 123456
<== Total: 1
User(id=2, name=lisi, pwd=123456)
false
Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@7f010382]
Returned connection 2130772866 to pool.
进程已结束,退出代码0
第二次开启二级缓存

- 结果如下:可以看出开启二级缓存后查询结果两个sqlsession产生的返回对象是同一个,且sql语句只调用了一次
Logging initialized using 'class org.apache.ibatis.logging.stdout.StdOutImpl' adapter.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
Cache Hit Ratio [com.zhang.dao.UserMapper]: 0.0
Opening JDBC Connection
Created connection 963110412.
==> Preparing: select * from mybatis.user where id=?
==> Parameters: 2(Integer)
<== Columns: id, name, pwd
<== Row: 2, lisi, 123456
<== Total: 1
User(id=2, name=lisi, pwd=123456)
Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@3967e60c]
Returned connection 963110412 to pool.
------------***************----------------
Cache Hit Ratio [com.zhang.dao.UserMapper]: 0.5
User(id=2, name=lisi, pwd=123456)
true
进程已结束,退出代码0
注意如果不进行缓存只读设置很可能会刷新第一次查询的缓存记录

- 根据下方的测试结果一,可以看出我们不设置readOnly="true"时,返回的user01,user02和user03都是不同的,
二级缓存是事务性的。这意味着,当 SqlSession 完成并提交时,或是完成并回滚,但没有执行 flushCache=true 的 insert/delete/update 语句时,缓存会获得更新。

- 根据下方的测试结果二,可以看出我们设置readOnly="true"时,返回的user01和user02是相同的,但user02和user03是不同的,
@Test
public void Test03(){
SqlSession sqlSession01 = MybatisUtils.getSqlSession();
SqlSession sqlSession02 = MybatisUtils.getSqlSession();
UserMapper mapper01 = sqlSession01.getMapper(UserMapper.class);
User user01 = mapper01.queryUserById(2);
System.out.println(user01.toString());
sqlSession01.close();
System.out.println("------------***************----------------");
UserMapper mapper02 = sqlSession02.getMapper(UserMapper.class);
User user02 = mapper02.queryUserById(2);
System.out.println(user02.toString());
System.out.println(user01==user02);
User user = new User();
user.setId(3);
user.setPwd("6113081");
user.setName("FT");
int i = mapper02.updateUser(user);
if (i >= 0) {
System.out.println("更新成功");
sqlSession02.commit();//事务一旦提交就会刷新缓存
}else{
System.out.println("更新失败");
sqlSession02.close();
}
User user03 = mapper02.queryUserById(2);
System.out.println(user03==user02);
sqlSession02.close();
}
测试结果一
Logging initialized using 'class org.apache.ibatis.logging.stdout.StdOutImpl' adapter.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
Cache Hit Ratio [com.zhang.dao.UserMapper]: 0.0
Opening JDBC Connection
Created connection 2061347276.
==> Preparing: select * from mybatis.user where id=?
==> Parameters: 2(Integer)
<== Columns: id, name, pwd
<== Row: 2, lisi, 123456
<== Total: 1
User(id=2, name=lisi, pwd=123456)
Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@7adda9cc]
Returned connection 2061347276 to pool.
------------***************----------------
As you are using functionality that deserializes object streams, it is recommended to define the JEP-290 serial filter. Please refer to https://docs.oracle.com/pls/topic/lookup?ctx=javase15&id=GUID-8296D8E8-2B93-4B9A-856E-0A65AF9B8C66
Cache Hit Ratio [com.zhang.dao.UserMapper]: 0.5
User(id=2, name=lisi, pwd=123456)
false
Opening JDBC Connection
Checked out connection 2061347276 from pool.
==> Preparing: update mybatis.user set name=?,pwd=? where id=?
==> Parameters: FT(String), 6113081(String), 3(Integer)
<== Updates: 1
更新成功
Cache Hit Ratio [com.zhang.dao.UserMapper]: 0.3333333333333333
==> Preparing: select * from mybatis.user where id=?
==> Parameters: 2(Integer)
<== Columns: id, name, pwd
<== Row: 2, lisi, 123456
<== Total: 1
false
Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@7adda9cc]
Returned connection 2061347276 to pool.
进程已结束,退出代码0
测试结果二
Logging initialized using 'class org.apache.ibatis.logging.stdout.StdOutImpl' adapter.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
Cache Hit Ratio [com.zhang.dao.UserMapper]: 0.0
Opening JDBC Connection
Created connection 963110412.
==> Preparing: select * from mybatis.user where id=?
==> Parameters: 2(Integer)
<== Columns: id, name, pwd
<== Row: 2, lisi, 123456
<== Total: 1
User(id=2, name=lisi, pwd=123456)
Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@3967e60c]
Returned connection 963110412 to pool.
------------***************----------------
Cache Hit Ratio [com.zhang.dao.UserMapper]: 0.5
User(id=2, name=lisi, pwd=123456)
true
Opening JDBC Connection
Checked out connection 963110412 from pool.
==> Preparing: update mybatis.user set name=?,pwd=? where id=?
==> Parameters: FT(String), 6113081(String), 3(Integer)
<== Updates: 1
更新成功
Cache Hit Ratio [com.zhang.dao.UserMapper]: 0.3333333333333333
==> Preparing: select * from mybatis.user where id=?
==> Parameters: 2(Integer)
<== Columns: id, name, pwd
<== Row: 2, lisi, 123456
<== Total: 1
false
Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@3967e60c]
Returned connection 963110412 to pool.
进程已结束,退出代码0
1.2.4 小结
二级缓存是mapper级别的缓存,它的实现机制跟一级缓存差不多,也是基于PerpetualCache的HashMap本地存储。作用域为mapper的namespace,可以自定义存储,比如Ehcache。Mybatis的二级缓存是跨Session的,每个Mapper享有同一个二级缓存域。
Mybatis内部存储缓存使用一个HashMap,key为hashCode+sqlId+Sql语句。value为从查询出来映射生成的Java对象。
mybatis缓存-二级缓存的更多相关文章
- Mybatis的二级缓存配置
一个项目中肯定会存在很多共用的查询数据,对于这一部分的数据,没必要每一个用户访问时都去查询数据库,因此配置二级缓存将是非常必要的. Mybatis的二级缓存配置相当容易,要开启二级缓存,只需要在你的 ...
- 使用Redis做MyBatis的二级缓存
使用Redis做MyBatis的二级缓存 通常为了减轻数据库的压力,我们会引入缓存.在Dao查询数据库之前,先去缓存中找是否有要找的数据,如果有则用缓存中的数据即可,就不用查询数据库了. 如果没有才去 ...
- mybatis一级缓存二级缓存
一级缓存 Mybatis对缓存提供支持,但是在没有配置的默认情况下,它只开启一级缓存,一级缓存只是相对于同一个SqlSession而言.所以在参数和SQL完全一样的情况下,我们使用同一个SqlSess ...
- MyBatis:二级缓存原理分析
MyBatis从入门到放弃七:二级缓存原理分析 前言 说起mybatis的一级缓存和二级缓存我特意问了几个身边的朋友他们平时会不会用,结果没有一个人平时业务场景中用. 好吧,那我暂且用来学习源码吧.一 ...
- Spring Boot + Mybatis + Redis二级缓存开发指南
Spring Boot + Mybatis + Redis二级缓存开发指南 背景 Spring-Boot因其提供了各种开箱即用的插件,使得它成为了当今最为主流的Java Web开发框架之一.Mybat ...
- mybatis的缓存机制(一级缓存二级缓存和刷新缓存)和mybatis整合ehcache
1.1 什么是查询缓存 mybatis提供查询缓存,用于减轻数据压力,提高数据库性能. mybaits提供一级缓存,和二级缓存. 一级缓存是SqlSession级别的缓存.在操作数据库时需要构造 s ...
- Mybatis的二级缓存注意点
--声明:一下内容都不一定是正确的,只是自己测试的结果,请自己的动手操作得出自己的结论 1.开启Mybatis的二级缓存,不仅要在SqlMapConfig.xml中进行开启总开关,还要在对应的XXXM ...
- mybatis开启二级缓存小记
mybatis开启二级缓存小记 1.开启二级缓存 和一级缓存默认开启不一样,二级缓存需要我们手动开启 首先在全局配置文件 mybatis-configuration.xml 文件中加入如下代码: &l ...
- 《深入理解mybatis原理7》 MyBatis的二级缓存的设计原理
<深入理解mybatis原理> MyBatis的二级缓存的设计原理 MyBatis的二级缓存是Application级别的缓存,它可以提高对数据库查询的效率,以提高应用的性能.本文将全面分 ...
- 《深入理解mybatis原理》 MyBatis的二级缓存的设计原理
MyBatis的二级缓存是Application级别的缓存,它可以提高对数据库查询的效率,以提高应用的性能.本文将全面分析MyBatis的二级缓存的设计原理. 如上图所示,当开一个会话时,一个SqlS ...
随机推荐
- Solution -「牛客 NOIP 模拟赛」打拳
\(\mathcal{Description}\) 现 \(2^n\) 个人进行淘汰赛,他们的战力为 \(1\sim 2^n\),战力强者能战胜战力弱者,但是战力在集合 \(\{a_m\}\) 里 ...
- Dapp开发petshop——truffle官方例程
truffle-pet-shop pet-shop是truffle的官方例程. 之前参考https://learnblockchain.cn/2018/01/12/first-dapp/的中文教程,但 ...
- ServiceStack.Redis的源码分析(连接与连接池)
前几天在生产环境上redis创建连接方面的故障,分析过程中对ServiceStack.Redis的连接创建和连接池机制有了进一步了解.问题分析结束后,通过此文系统的将学习到的知识点整理出来. 从连接池 ...
- SpringCloud微服务实战——搭建企业级开发框架(三十八):搭建ELK日志采集与分析系统
一套好的日志分析系统可以详细记录系统的运行情况,方便我们定位分析系统性能瓶颈.查找定位系统问题.上一篇说明了日志的多种业务场景以及日志记录的实现方式,那么日志记录下来,相关人员就需要对日志数据进行 ...
- .NET6: 开发基于WPF的摩登三维工业软件 (7)
做为一个摩登的工业软件,提供可编程的脚本能力是必不可少的能力.脚本既可以方便用户进行二次开发,也对方便对程序进行自动化测试.本文将结合AnyCAD对Python脚本支持的能力和WPF快速开发带脚本编辑 ...
- C# 逆变(Contravariance)/协变(Covariance) - 个人的理解
逆变(Contravariance)/协变(Covariance) 1. 基本概念 官方: 协变和逆变都是术语,前者指能够使用比原始指定的派生类型的派生程度更大(更具体的)的类型,后者指能够使用比原始 ...
- VUE3 之 使用标签实现动画与过渡效果 - 这个系列的教程通俗易懂,适合新手
1. 概述 巴纳姆效应告诉我们: 人们更容易相信笼统的.常见的人格描述,并觉得特别适合自己,认为该描述真实地反映了自己的人格面貌. 这也是所有算命先生的小把戏,算命先生通常把话说的很笼统,很通用,基本 ...
- python3中zip()的用法
zip函数接受任意多个可迭代对象作为参数,将对象中对应的元素打包成一个tuple,然后返回一个可迭代的zip对象. 这个可迭代对象可以使用循环的方式列出其元素 若多个可迭代对象的长度不一致,则所返回的 ...
- QUIC协议详解
声明 本文可以自由转载但需注明原始链接.本文为本人原创,作者LightningStar,原文发表在博客园.本文主体内容参考论文[1]完成. 介绍 QUIC,发音同quick,是"Quick ...
- AutoResetEvent 与 ManualResetEvent
实际上这两个东西是同一种东西,可以把他们理解为线程锁,两个不同的线程可以共享. 这两个类的构造函数参数都是传入一个 bool 值,这个 bool 值可以理解为一开始的时候,这个需要访问的资源是处于可用 ...