固态硬盘缺陷:无法长时间使用,而磁盘只要不消磁,只要不受到磁影响,就可以长期使用,因此绝大多数企业还是使用磁盘来存储数据
像mysql这种关系型数据库中的数据存储在磁盘中,为方便查询,减少系统开销,提高查询效率,会在内存中开辟空间用于存储经常查询、但不怎么改变的数据,这块空间称为缓存
MyBatis缓存分类
- 一级缓存
- 二级缓存
前置知识,MyBatis是如何执行的
private InputStream in = null;
private SqlSession session = null;
private UserDao mapper = null;
@Before //前置通知, 在方法执行之前执行
public void init() throws IOException {
//加载主配置文件,目的是为了构建SqlSessionFactory对象
in = Resources.getResourceAsStream("SqlMapConfig.xml");
//创建SqlSessionFactory对象
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
//通过SqlSessionFactory工厂对象创建SqlSesssion对象
session = factory.openSession();
//通过Session创建UserDao接口代理对象
mapper = session.getMapper(UserDao.class);
}
@After //@After: 后置通知, 在方法执行之后执行 。
public void destory() throws IOException {
//释放资源
session.close();
in.close();
}
}
1.一级缓存
默认情况下开启的缓存 放在SqlSession对象中
什么是对象→堆里面的一块内存空间
上述的SqlSession对象也是堆里面的一块空间,SqlSession对象里有一块一级缓存空间(SqlSession对象所在空间∈内存,cache缓存所在空间∈SqlSession对象所在空间,因此,之前说缓存是内存中的一块空间)
两次查询同一个数据,sql语句只会执行一次,第二次会从缓存中直接获取数据
@Test
public void findById(){
User user1 = mapper.findById(1);
User user2 = mapper.findById(1);
System.out.println(user1.toString());
System.out.println(user2.toString());
System.out.println(user1 == user2); //在引用数据类型当中 == 是用来比较什么的? 是否指向同一个内存地址
}
一级缓存失效的四种情况
1.sqlSession不同
一个SqlSession可以生成多个session对象,继而创建多个mapper
private InputStream in = null;
private SqlSession session = null;
private SqlSession session2 = null;
private UserDao mapper = null;
private UserDao mapper2 = null;
@Before //前置通知, 在方法执行之前执行
public void init() throws IOException {
//加载主配置文件,目的是为了构建SqlSessionFactory对象
in = Resources.getResourceAsStream("SqlMapConfig.xml");
//创建SqlSessionFactory对象
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
//通过SqlSessionFactory工厂对象创建SqlSesssion对象
session = factory.openSession();
session2 = factory.openSession();
//通过Session创建UserDao接口代理对象
mapper = session.getMapper(UserDao.class);
mapper2 = session2.getMapper(UserDao.class);
}
@After //@After: 后置通知, 在方法执行之后执行 。
public void destory() throws IOException {
//释放资源
session.close();
in.close();
}
/*
* sqlSession不同
* 两个mapper不是同一个session生成的,故对应的开辟的内存空间也不同
* */
@Test
public void findById5(){
User user1 = mapper.findById(1);
User user2 = mapper2.findById(1);
System.out.println(user1 == user2);
}
}
两个mapper不是同一个session生成的,故对应的开辟的内存空间也不同
2.sqlSession相同,查询条件不同,多次查询不同的情况,不会导致缓存失效
其实也很好理解,第一次查询的数据存储在缓存中,但第二次查询查询的是与第一次不同的数据,那缓存中必然是没有第二次查询的数据的
/*
sqlSession相同,查询条件不同
*/
@Test
public void findById1(){
User user1 = mapper.findById(1);
User user2 = mapper.findById(2);
System.out.println(user1.toString());
System.out.println(user2.toString());
System.out.println(user1 == user2);
}
3.sqlSession相同,查询的数据也相同,但两次查询之间执行了增删改操作
缓存要和数据库保持一致,如果进行了增删改操作却不修改缓存,那么可能造成缓存和数据库不一致的情况,所以执行增删改操作后缓存便失效了
/**
* sqlSession相同,查询的数据也相同,但两次查询之间执行了增删改操作
* 缓存要和数据库保持一致,如果进行了增删改操作不修改缓存,那么可能造成缓存和数据库不一致的情况!
*/
@Test
public void findById2(){
User user1 = mapper.findById(1);
mapper.insert(user1); //两次查询之间执行了增删改操作!
User user2 = mapper.findById(1);
System.out.println(user1 == user2);
}
4.sqlSession相同,手动清除一级缓存
/*
* sqlSession相同,手动清除一级缓存
* */
@Test
public void findById3(){
User user1 = mapper.findById(1);
session.clearCache(); //清除缓存
User user2 = mapper.findById(1);
System.out.println(user1 == user2);
}
session.clearCache()
这个也很好理解,第一次查询后缓存被清除了,第二次查询相同的数据时,缓存中已经没有了相应的数据,必然是要重新执行sql语句进行查询,开辟了新的内存空间,故指向地址也不一致了
2.二级缓存
二级缓存是SqlSessionFactory级别,通过同一个SqlSessionFactroy创建SqlSession查询结果会被缓存;此后若再次执行相同的查询语句,结果会从一个缓存中获取。
(1)二级缓存开启的条件
①:在核心配置文件中,设置全局属性caheEnabled="true"
。
②:在mapper映射文件中配置<cache/>
③:查询数据所转换的实体类类型必须实现序列化接口 implements Serializable
④:二级缓存必须在SqlSession(一级缓存)关闭或提交之后有效
(2)开启二级缓存
①.在SqlMapConfig.xml配置文件中开启二级缓存
<!‐‐ 开启二级缓存 ‐‐>
<settings>
<!--开启二级缓存-->
<setting name="cacheEnabled" value="true"/>
</settings>
②. 在UserMapper.xml配置文件声明使用二级缓存
<!--使用二级缓存-->
<cache/>
③:查询数据所转换的实体类类型必须实现序列化接口
public class User implements Serializable{
......
}
④:二级缓存必须在SqlSession关闭或提交之后有效
@Test
public void findById4(){
User user1 = mapper.findById(1);
//关闭一级缓存
session.close();
User user2 = mapper2.findById(1);
session2.close();
System.out.println(user1 == user2); // 一级缓存缓存的是对象,二级缓存缓存的是数据
}
打印发现2个对象的地址值不一样,但是确实只发送了一次SQL语句的查询,二级缓存中存储的是数据,不是对象
一级缓存缓存的是对象(俩对象地址相同则相等),而二级缓存缓存的是数据,数据无法直接对比是否相同,故为false
(3)二级缓存失效的情况
两次查询之间行了任意的增删改,会使得一级二级缓存同时失效
这点与一级缓存一致,故不再赘述
(4)Catch参数的具体细节
缓存空间也是有限的,当满了之后,再进来新的数据,就要将之前的数据进行移除
eviction(收回策略)
- LRU(最近最少使用的):移除最长时间不被使用的对象,这是默认值。
- FIFO(先进先出):按对象进入缓存的顺序来移除它们。
- SOFT(软引用):移除基于垃圾回收器状态和软引用规则的对象。
- WEAK(弱引用):更积极地移除基于垃圾收集器状态和弱引用规则的对象。
flushinterval(刷新间隔)
可以被设置为任意的正整数,而且它们代表一个合理的毫秒形式的时间段。
- 默认情况不设置,即没有刷新间隔,缓存仅仅在调用语句时刷新。
size(引用数目)
- 可以被设置为任意正整数,要记住缓存的对象数目和运行环境的可用内存资源数目。默认值是1024 。
readOnly(只读)
属性可以被设置为 true / false。
- true:只读缓存:**会给所有调用者返回缓存对象的相同实例。**因此这些对象不能被修改, 这提供了很重要的性能优势。
- false读写缓存: 通过序列化返回缓存对象的拷贝,这种方式会慢一些,但是安全,因此默认是 false。
配置结果
<cache eviction="FIFO" flushInterval="6000" size="512" readOnly="true"/>
3.Mybatis缓存查询顺序
- 先查询二级缓存,因为二级缓存中可能会有其他程序查询出来的数据,可以直接拿来使用
- 如果二级缓存未命中,再查询一级缓存
- 如果一级缓存也没有命中,则查询数据库
- SqlSession关闭之后,一级缓存的数据会写入二级缓存