面试最爱问这些,缓存的作用和机制啦,特别是MyBatis还有跟Redis结合的部分!潸然泪下!希望多年以后,我看到我写的这些笔记,能很“不屑”地说一句,“诶呀,不过就是balabala”哈哈哈
MyBatis的缓存机制
MyBatis的一级缓存
一级缓存是SqlSession级别的,通过同一个SqlSession查询的数据会被缓存,下次查询相同的数据,就会从缓存中直接获取,不会从数据库重新访问。
使一级缓存失效的四种情况:1) 不同的 SqlSession 对应不同的一级缓存2) 同一个 SqlSession 但是查询条件不同3) 同一个 SqlSession 两次查询期间执行了任何一次增删改操作4) 同一个 SqlSession 两次查询期间手动清空了缓存
一级缓存也称为本地缓存,第一篇笔记debug的时候,如果有印象,应该见过localCache这个变量,当MyBatis设置完参数和参数值映射关系,准备执行SQL时,当查询二级缓存和一级缓存都没有命中时,就会直接从数据库查询了,查询返回的结果,会先放到一级缓存中,然后再返回。
localCache.putObject(key, EXECUTION_PLACEHOLDER) 先在缓存中占个位置
try查询返回结果list前,先执行finally,把这个占的位置拿出来removeObject
localCache.putObject(key, list),正式把查询结果放到本地缓存中
key就是hashCode + 查询的sql id + sql语句 + 参数

当sqlSession flush或close后,sqlSession中的localCache就会被清空。
本地缓存不能被关闭,但可以调用clearCache()清空本地缓存,或改变缓存的作用域(比如二级缓存若开启的话,一级缓存的东西就会放到二级缓存了)我还以为是这个意思呢
mybatis3.1之后可以配置本地缓存的作用域:mybatis.xml
MyBatis利用本地缓存机制(Local Cache)防止循环引用(循环引用)和加速重复嵌套查询。
属性localCacheScope,可选项SESSION | STATEMENT,默认是SESSION,会缓存一个sqlSession中执行的所有查询,但如果是STATEMENT,官方的说法是本地会话仅在语句执行上,对同一个sqlSession的不同调用,将不会共享数据。
总的来说:在update、insert、delete、flushCache=“true”、commit、rollback、LocalCacheScope.STATEMENT等情况下,一级缓存就都会被清空。
演示一级缓存的功力~~~
相同sqlSession、相同mapper对象或者不同mapper对象都会使用一级缓存:


演示一级缓存失效的几种情况
1) 不同的SqlSession对应不同的一级缓存

2) 同一个SqlSession但是查询条件不同

3) 同一个SqlSession两次查询期间执行了任何一次增删改操作

注意!!!这里增删改不一定是这个mapper,任意mapper有增删改都会使本地缓存刷新掉。

4) 同一个SqlSession两次查询期间手动清空了缓存

MyBatis的二级缓存
二级缓存是SqlSessionFactory级别,通过同一个SqlSessionFactory创建的SqlSession查询的结果会被缓存;此后若再次执行相同的查询语句,结果就会从缓存中获取。
在尚硅谷2018年发布的教程课件中,也有说法二级缓存是namespace级别的,也就是Mapper级别的,这个看怎么理解,二级缓存和一级缓存其实是有交叉的,并不是包含的关系,但namespace一致的情况下,二级缓存包含了所有sqlSession的本地缓存,这么理解,是不是就是sqlSessionFactory级别的?
为了更好地扩展功能,也可以通过实现cache接口自定义二级缓存。
二级缓存开启的条件:a> 在核心配置文件中,设置全局配置属性 cacheEnabled="true" ,默认为 true ,不需要设置b> 在映射文件中设置标签 <cache />
二级缓存(second level cache),全局作用域缓存,默认不开启,需手动配置
二级缓存只有在sqlSession关闭或提交之后才会生效
演示一下二级缓存的功力~~~
<!--设置全局配置属性cacheEnabled="true",默认为true,不需要设置-->
<settings>
<setting name="cacheEnabled" value="true"/>
</settings>
在映射文件中设置标签<cache/>或<cache></cache>

于是不出意外的话,就出意外了;感受到了二级缓存,但命中率怎么都是0!!!

原因就在获取sqlSession的这个工具类!!!这个时候,就可以理解上面说的二级缓存是SqlSessionFactory级别,通过同一个SqlSessionFactory创建的SqlSession查询的结果会被缓存。而这里工具类中,每次调用sqlSession,sqlSessionFactory都是全新的,上面两个SQL根本就是在两个sqlSessionFactory中,也就是涉及两个二级缓存。
public class SqlSessionUtil {
public static SqlSession getSqlSession() {
InputStream inputStream = null;
try {
inputStream = Resources.getResourceAsStream("mybatis-config.xml");
} catch (IOException e) {
throw new RuntimeException(e);
}
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession(true);
return sqlSession;
}
}
因此,我们不能再使用这个工具类去获取sqlSession了。
这里可以看到Cache Hit Ratio [com.coffeeship.mapper.UserMapper]: 0.5,命中了
这里为什么是0.5呢,因为第一次查询算一次,第二次本身是一次,这样就是0.5,再查一次,命中率就是2/3了。
然后为什么这里返回两个对象是false呢,那是因为二级缓存的配置中,我使用的都是默认的配置,比方说readOnly,默认是false,会返回缓存对象的拷贝。
这里我们可以试着更改一下二级缓存配置:
<cache readOnly="true"></cache>
这里返回的就是缓存对象的相同实例,因此两个对象就是一样的。
使二级缓存失效的情况:两次查询之间执行了任意的增删改,会使一级和二级缓存同时失效
这里千万注意!!!又感觉2018那个教程说的二级缓存是namespace级别的,是对的了。这里我尝试在EmpMapper名称空间进行增加数据操作,发现UserMapper那还是用到了二级缓存。
但UserMapper名称空间发生增加数据操作后,虽然命中率

最低0.47元/天 解锁文章

被折叠的 条评论
为什么被折叠?



