缓存(Cache)就是数据交换的缓冲区,一个临时存储数据的地方,当我们读取数据时会首先从缓存中查找需要的数据,如果找到了则直接执行,找不到的话再从内存中找。
在实际开发中,我们会经常对数据库进行数据查询,而从数据库读取数据的效率是非常低下的,并且频繁地去访问数据库会增大数据库压力降低数据库查询性能等,所以我们可以将经常查询且不经常改变的数据保存到缓存中(缓存就是内存中的一个对象),这样用户在查询的时候就不用到数据库中查询(磁盘),从而减少与数据库的交付次数,从而提高查询效率,解决了高并发系统的性能问题。
缓存的本质就是用空间换时间,牺牲数据的实时性,以服务器内存中的数据暂时代替从数据库读取最新的数据,减少数据库IO,减轻服务器压力,减少网络延迟,从而提高访问速度。

Mybatis中设计了缓存机制来提升数据的检索效率,避免每一次数据的检索都去查询数据库
一级缓存是SqlSession级别的缓存也叫本地缓存,因为每一个用户在执行查询的时候都需要使用SqlSession来执行,为了避免每一次都去查询数据库,Mybatis把查询出来的数据缓存到SqlSession的本地缓存中,后续的SQL如果在命中缓存的情况下就可以直接从本地缓存去读取数据,如果想要去实现跨SqlSession级别的一个缓存那么一级缓存是无法做到的,因此在Mybatis中引入了二级缓存的一个设计,也就是说当多个用户在查询数据的时候,只要有任何一个SqlSession拿到了数据就会放入到二级缓存里面,那么其他的SqlSession就可以直接从二级缓存中去加载数据
一级缓存在操作数据库时需要构造SqlSession对象,在对象中有一个 数据结构(HashMap)用于存储缓存数据,不同的SqlSession之间的缓存数据区域(HashMap)互不影响。
当在同一个sqlSession (会话) 中执行两次相同的SQL语句时,第一次执行完毕会将从数据库中查询的数据写到缓存(内存),第二次查询时会从缓存中获取数据,不再去底层数据库查询,从而提高查询效率。需要注意的是,如果sqlSession执行了DML操作(insert、update、delete),并提交到数据库,MyBatis则会清空sqlSession中的一级缓存,这样做的目的是为了保证缓存中存储的是最新的信息,避免出现脏读现象。
MyBatis默认开启一级缓存,不需要进行任何配置,当一个sqlSession结束后该sqlSession的一级缓存也就不在了,一级缓存是不能关闭的。
小结:
一级缓存(本地缓存), 作用域默认为sqlSession。当 Session flush 或 close 后, 该Session 中的所有Cache 将被清空。
本地缓存不能被关闭, 但可以调用clearCache()来清空本地缓存, 或者改变缓存的作用域。
在mybatis3.1之后,可以配置本地缓存的作用域,在 mybatis.xml 中配置。
让一级缓存失效的几种情况:
① 不同的SqlSession对应不同的一级缓存
② 同一个SqlSession但是查询条件不同
③ 同一个SqlSession的两次查询期间执行了增删改操作
④ 同一个SqlSession的两次查询期间手动清空了缓存
二级缓存也叫全局缓存,一级缓存作用域太低了,二级缓存默认是全局开启的,它是基于namespace级别的缓存,一个名称空间,对应一个二级缓存,所以也称之为“namespace缓存”,需要在配置SQL语句的XML中添加节点, 以表示当前XML中的所有查询都允许开通二级缓存,并且,在节点上配置useCache=“true”,则对应的节点的查询结果将被二级缓存处理,并且,此查询返回的结果的类型必须是实现了Serializable接口的,如果使用了配置如何封装查询结果,则必须使用节点来封装主键的映射,满足以上条件后,二级缓存将可用,只要是当前namespace中查询出来的结果,都会根据所执行的SQL语句及参数进行 结果的缓存
开启二级缓存具体步骤:
<!-- 全局配置参数,需要时再设置 -->
<settings>
<!-- 开启二级缓存 默认值为true -->
<setting name="cacheEnabled" value="true"/>
</settings>
在mapper.xml配置文件中使用二级缓存
<mapper namespace="com.wenjie.mapper.UserMapper">
<cache/> <!-- 二级缓存生效 -->
<select id="selectAll" resultType="user">
select *
from tb_user;
</select>
<select id="selectById" resultType="user">
select *
from tb_user where id = #{id};
</select>
</mapper>
也可以直接在mapper.xml文件中加入,但是要记得实体类要序列化,不然容易会报Caused by: java.io.NotSerializableException: com.xsq.pojo.User异常
public class User implements Serializable {
//Serializable实现序列化,为了将来反序列化
}
运行程序进行测试,控制台输出结果如下所示:

只进行了一次查询,那么就说明数据已经进入到了二级缓存中。
对于缓存数据更新机制,当某一个作用域(一级缓存 Session/二级缓存Namespaces)的进行了新增、修改、删除操作后,默认该作用域下所有 select 中的缓存将被 clear。
工作机制
小结:

一级缓存:在SqlSession中会持有一个Executor,每个Executor里面会有个叫LocalCache的一个对象,当用户发起查询的时候,Mybatis会去根据执行语句在LocalCache里面去查找,如果命中了就直接把这个数据返回,如果没有命中再去数据库里面去查询出来最后写入到LocalCache里面,所以一级缓存的生命周期是SqlSession
二级缓存:在原来的Executor上去做了一个装饰,引入了一个叫CachingExecutor的装饰器,所以在进入一级缓存的查询之前会先通过CachingExecutor进行二级缓存的查询,开启二级缓存之后会被多个SqlSession共享,所以它是一个全局的缓存,所以它的查询流程就变成了先查二级缓存再来查一级缓存最后再去查数据库,另外Mybatis的二级缓存相对于一级缓存来说它实现SqlSession之间的缓存数据的共享同时缓存粒度也能够控制到namespace一个级别,并且还可以通过Cache这样一个接口来实现不同缓存实现类的一个组合,对Cache的可控性也更高了
无论是一级缓存还是二级缓存,只要数据发生了写操作(增、删、改), 缓存数据都将被自动清理。由于Mybatis的缓存清理机制过于死板,所以,一般在开发实践中并不怎么使用!更多的是使用其它的缓存工具并自行制定缓存策略