发布于2021-05-29 22:36 阅读(1417) 评论(0) 点赞(28) 收藏(1)
缓存就是数据交换的缓存区(称作Cache),是存储数据(使用频繁的数据)的临时地方。当用程序查询数据时,首先会在缓存中寻找,如果找到了就直接返回。如果找不到,则会去数据库中查找。缓存的本质就是用空间换时间,牺牲数据的实时性,以服务器内存中的数据暂时代替从数据库中读取的最新数据,减少程序与数据库之间的IO,减轻服务器压力,减少网络延迟,加快页面打开速度。
MyBatis支持声明式数据缓存( declarative data caching )。当一条SQL语句被标记为“可缓存”后,首次执行它时从数据库中获取的所有数据会被存储在一段高速缓存中,今后执行这条语句时就会从高速缓存中读取结果,而不是再次命中数据库。
MyBatis提供了默认下基于Java HashMap的缓存实现,以及用于与OSCache,Ehcache,Hazelcast和Mecached连接的默认连接器。MyBatis还提供API供其他缓存实现使用。
注意了!
上面内容的重点:MyBatis执行SQL语句之后,这条语句从数据库中获取的数据就会被缓存,后面再需要用到这些数据的时候,会直接从缓存中拿结果,而不是再次执行SQL。
MyBatis提供了两种缓存(一级缓存和二级缓存),接下来将对这两种缓存进行详细的介绍。
默认情况下,MyBatis只启用了一级缓存(也称为本地缓存),一级缓存的作用域是SqlSession。它仅仅对一个会话中的数据进行缓存。
接下来我们分情况对一级缓存进行测试说明
public class Test {
@org.junit.Test
public void test1() throws IOException {
InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession session1 = sqlSessionFactory.openSession();
try {
User user1 = session1.selectOne("com.ljt.mybatis.dao.UserDao.getUserById", 3);
System.out.println(user1);
User user2 = session1.selectOne("com.ljt.mybatis.dao.UserDao.getUserById", 3);
System.out.println(user2);
System.out.println(user1 == user2);
} finally {
session1.commit();
session1.close();
}
}
}
日志打印结果:
第一次查询,执行了SQL语句,然后返回了查询结果,第二次查询并没有执行SQL语句,直接从缓存中获取到了结果;通过"=="运算符对user1和user2进行比较,返回true,发现user1和user2实际上指向的是同一个对象。
结论:同个SqlSession进行两次相同查询,MyBatis只进行一次数据库查询,第二次会直接从缓存中获取查询结果。
public class Test {
@org.junit.Test
public void test1() throws IOException {
InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession session1 = sqlSessionFactory.openSession();
try {
User user1 = session1.selectOne("com.ljt.mybatis.dao.UserDao.getUserById", 3);
System.out.println(user1);
User user2 = session1.selectOne("com.ljt.mybatis.dao.UserDao.getUserById", 2);
System.out.println(user2);
System.out.println(user1 == user2);
} finally {
session1.commit();
session1.close();
}
}
}
日志打印结果:
两次查询都执行了SQL语句;通过"=="运算符对user1和user2进行比较,返回false,user1和user2是两个不同的对象。
结论:同个SqlSession进行两次不同的查询,会执行两次SQL。
额,实际上这里并没有使用到缓存。。。
不过这应该在大家的意料之中吧,查询条件不同,第二次查询的数据缓存中根本没有,当然会执行SQL从数据库中取数据了!
public class Test {
@org.junit.Test
public void test1() throws IOException {
InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession session1 = sqlSessionFactory.openSession();
SqlSession session2 = sqlSessionFactory.openSession();
try {
User user1 = session1.selectOne("com.ljt.mybatis.dao.UserDao.getUserById", 3);
System.out.println(user1);
User user2 = session2.selectOne("com.ljt.mybatis.dao.UserDao.getUserById", 3);
System.out.println(user2);
System.out.println(user1 == user2);
} finally {
session1.commit();
session2.commit();
session1.close();
session2.close();
}
}
}
日志打印结果:
两次查询都执行了SQL语句;通过"=="运算符对user1和user2进行比较,返回false,user1和user2是两个不同的对象。
结论:不同SqlSession,进行相同查询,会执行两次SQL。
也就是说,这里也没有用到缓存。一级缓存作为SqlSession级别的缓存,每一个SqlSession都有自己独立的缓存,不同的SqlSession之间的缓存,并不共享。
public class Test {
@org.junit.Test
public void test1() throws IOException {
InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession session1 = sqlSessionFactory.openSession();
try {
User user1 = session1.selectOne("com.ljt.mybatis.dao.UserDao.getUserById", 3);
System.out.println(user1);
session1.update("com.ljt.mybatis.dao.UserDao.setNameById",3);
User user2 = session1.selectOne("com.ljt.mybatis.dao.UserDao.getUserById", 3);
System.out.println(user2);
System.out.println(user1 == user2);
} finally {
session1.commit();
session1.close();
}
}
}
日志打印结果:
两次查询都执行了SQL语句;通过"=="运算符对user1和user2进行比较,返回false,user1和user2是两个不同的对象。
结论:更新操作之后缓存会被清除。
既然数据更新了,那么第二次查询时,再使用缓存中过时的数据不就毫无意义了吗?
public class Test {
@org.junit.Test
public void test1() throws IOException {
InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession session1 = sqlSessionFactory.openSession();
try {
User user1 = session1.selectOne("com.ljt.mybatis.dao.UserDao.getUserById", 3);
System.out.println(user1);
session1.clearCache();
User user2 = session1.selectOne("com.ljt.mybatis.dao.UserDao.getUserById", 3);
System.out.println(user2);
System.out.println(user1 == user2);
} finally {
session1.commit();
session1.close();
}
}
}
日志打印结果:
两次查询都执行了SQL语句;通过"=="运算符对user1和user2进行比较,返回false,user1和user2是两个不同的对象。
结论:缓存清除后,即使在同一个SqlSession中执行相同的查询,还是会执行两次SQL。
这不废话嘛,缓存清除了,再次查找的话当然得去数据库中查找了。
1.在同一个 SqlSession中进行多次相同查询,只会在第一次查询时执行SQL;
2.不同的 SqlSession 之间的缓存是相互独立的;
3.可以使用clearCache()清空已有缓存;
4.任何的更新语句( UPDATE, INSERT, DELETE 语句)都会清空缓存。
之所以称之为“二级缓存”,是相对于“一级缓存”而言的。
二级缓存是Mapper级别的缓存,多个SqlSession去操作同一个Mapper的sql语句,多个SqlSession可以共用二级缓存,二级缓存是跨SqlSession的。
要启用全局的二级缓存,只需要在SQL映射文件中添加一行:
<cache/>
是不是很简单?
我来给大家解释一下这个简单语句产生的作用:
映射语句文件中的所有 select 语句的结果将会被缓存。
映射语句文件中的所有 insert、update 和 delete 语句会刷新缓存。
缓存会使用最近最少使用算法(LRU, Least Recently Used)算法来清除不需要的缓存。
缓存不会定时进行刷新(也就是说,没有刷新间隔)。
缓存会保存列表或对象的1024个引用。
缓存会被视为读/写缓存,这意味着获取到的对象并不是共享的,可以安全地被调用者修改,而不干扰其他调用者或线程所做的潜在修改。
这些属性可以通过 cache 元素的属性来修改。比如:
<cache
eviction="FIFO"
flushInterval="60000"
size="512"
readOnly="true"/>
下面对cache中常用的属性进行介绍:
eviction(清除策略)
可用的清除策略有:
LRU – 最近最少使用:移除最长时间不被使用的对象。
FIFO – 先进先出:按对象进入缓存的顺序来移除它们。
SOFT – 软引用:基于垃圾回收器状态和软引用规则移除对象。
WEAK – 弱引用:更积极地基于垃圾收集器状态和弱引用规则移除对象。
默认的清除策略是 LRU。
flushInterval(刷新间隔)
属性可以被设置为任意的正整数,设置的值应该是一个以毫秒为单位的合理时间量。 默认情况是不设置,也就是没有刷新间隔,缓存仅仅会在调用语句时刷新。
size(引用数目)
属性可以被设置为任意正整数,要注意欲缓存对象的大小和运行环境中可用的内存资源。默认值是 1024。
readOnly(只读)
属性可以被设置为 true 或 false。只读的缓存会给所有调用者返回缓存对象的相同实例。 因此这些对象不能被修改。这就提供了可观的性能提升。而可读写的缓存会(通过序列化)返回缓存对象的拷贝。 速度上会慢一些,但是更安全,因此默认值是 false。
讲到这里,大家应该对二级缓存有些基本了解了;
不过既然有了一级缓存,那么为什么要提供二级缓存呢?
我们知道,在一级缓存中,不同SqlSession进行相同SQL查询的时候,是查询两次数据库的。显然这是一种浪费,既然SQL查询相同,就没有必要再次查库了,直接利用缓存数据即可,这就是MyBatis二级缓存的初衷。
另外,Spring和MyBatis整合时,每次查询之后都要进行关闭SqlSession,关闭之后数据被清空。所以MyBatis和Spring整合之后,一级缓存是没有意义的。
可如果开启二级缓存,关闭SqlSession后,会把该SqlSession一级缓存中的数据添加到mapper namespace的二级缓存中。
这样,缓存在SqlSession关闭之后依然存在。
原文链接:https://blog.csdn.net/qq_51372098/article/details/117309702
作者:快起来搬砖啦
链接:http://www.javaheidong.com/blog/article/207631/35f80c1d7b3612f36399/
来源:java黑洞网
任何形式的转载都请注明出处,如有侵权 一经发现 必将追究其法律责任
昵称:
评论内容:(最多支持255个字符)
---无人问津也好,技不如人也罢,你都要试着安静下来,去做自己该做的事,而不是让内心的烦躁、焦虑,坏掉你本来就不多的热情和定力
Copyright © 2018-2021 java黑洞网 All Rights Reserved 版权所有,并保留所有权利。京ICP备18063182号-2
投诉与举报,广告合作请联系vgs_info@163.com或QQ3083709327
免责声明:网站文章均由用户上传,仅供读者学习交流使用,禁止用做商业用途。若文章涉及色情,反动,侵权等违法信息,请向我们举报,一经核实我们会立即删除!