1.为什么要做缓存?

先说普通的缓存,比如redis、memcache,使用这些缓存,是为了缓存“热点数据”,防止每次都从DB获取,或者防止每次都进行复杂开销大的计算(比如某个结果需要耗时5秒计算);mybatis只对DB返回的数据进行缓存,防止每次都查询DB(可以缓解DB的一部分压力);
image.png

2.认识mybatis的缓存

mybatis的缓存可以理解为一个map,map的key由namespace、statement-id、offset、limit、sql、env共同决定。mybatis中,有两种缓存策略,也就是一级缓存和二级缓存
mybatis的一级缓存,缓存的数据,作用域范围是同一个SqlSession;
mybatis的二级缓存,缓存的数据,作用域范围是同一个SqlSessionFactory创建的所有SqlSession。
在说这两种缓存的区别前,先简单提一下mybatis的几个关键对象:SqlSession可以认为是一个数据库会话(连接),由SqlSessionFactory创建,而SqlSessionFactory又是由SqlSessionFactoryBuilder创建。

2.1一级缓存介绍

mybatis中,默认是开启一级缓存的
一级缓存的作用域范围是同一个SqlSession,一级缓存的数据其实是存在SqlSession的Executor对象中,在Executor对象中使用一个HashMap来保存缓存,所以不会有并发问题,因为执行操作的都是同一个SqlSession,同一时刻只有一个线程在使用该SqlSession;同一个SqlSession对象(前提),会利用namespace、statement-id、offset、limit、sql、env组合为key,然后去缓存中查询是否有缓存数据。

2.2一级缓存特点

1.一级缓存的Executor中使用HashMap来保存缓存数据,并不会有并发问题,因为执行操作的都是同一个SqlSession,同一时刻只有一个线程在使用该SqlSession;
2.一级缓存没有容量,新查询的结果总会缓存;
3.一级缓存数据没有过期限制(或者说更新机制),也就是说mybatis查询数据进行缓存后,通过其他方式(比如mysql命令行或者客户端工具)来修改数据库数据,那么mybatis将会一直使用缓存的数据,而不会检测到数据其实已经发生了变化。但是使用mybatis来进行更新数据时,会更新缓存数据,比如,下面这个过程:
1).启动mybatis程序(一直保持运行状态),mybatis查询id为1的数据,name为abc;
2).查询第二次,在缓存中查询到,name为abc;
3).登录mysql客户端(比如navicate或者mysql命令行),修改id为1的数据,将name修改为xyz;
4).使用mybatis进行第三次查询,仍旧从缓存中查询到数据,name仍旧为abc,此时就出现了数据不一致的问题。
5).使用mybatis来更新id为1的数据,将name修改为xxx;
6).使用mybatis进行第4次查询,查询db,获取到了最新的name为xxx。

2.3二级缓存介绍

当多个用户请求某同一数据,由于执行的sql都是一样的,但是由于是不同的用户,所以,就会产生多个SqlSession(不考虑使用数据库连接池的情况),多个SqlSession调用同一个id的方法,仍然不会使用缓存,这就存在资源浪费的问题:既然他们需要的数据是一样的,那么何必再重复查询一次呢,如果数据量很大,重复查询多次,岂不是耗费更多的资源?此时就用到了二级缓存

2.4一级缓存与二级缓存相比

一级缓存是指同一个SqlSession的作用域,缓存的数据存在每一个SqlSession各自的空间中(准确的说是保存在每个SqlSession各自的Executor中)。
而二级缓存的作用域,则是同一个SqlSessionFactory对象创建出的所有SqlSession,不同的SqlSessionFactory创建的SqlSession不能共享二级缓存(即使namespace、statement-id、offset、limit、sql、env都相同,也不能共享)。

2.5二级缓存的原理

二级缓存其实是使用CachingExecutor来包装一下Executor,使用的流程如下:
1.每次执行Executor的时候,先在CachingExecutor的进行二级缓存的查询;
2.如果二级缓存没有,则在进行一级缓存的查询,如果一级缓存有,则将一级缓存数据写入二级缓存;
3.如果一级缓存没有,则进行数据库查询,然后写入查询的数据到一级缓存,然后再写入二级缓存;
4.将查询结果返回。
开启二级缓存后,同一个namespace下的所有sql的查询结果保存在一个cache中,原理图如下所示:
mibatis缓存策略 - 图2

2.6、缓存优先级

当开启二级缓存后,一级缓存和二级缓存就同时存在,这个时候,mybatis应该先查那个cache呢?
答案是:二级缓存 > 一级缓存 > DB。

2.7二级缓存特点:

1.一级缓存的数据写入二级缓存的前提:一个SqlSession调用close()之后,才会将一级缓存中的数据放到二级缓存中,如果不关闭SqlSession,数据只会存在于一级缓存中;
2.二级缓存是基于namespace进行共享的,如果缓存多表联查的结果,那么二级缓存的数据可能不会及时更新,会有脏数据的风险。
对于第二点,举个例子:
1).进行一次查询,cn.ganlixin.mapper.UserDep命名空间,该空间连表查询user和dep表,将查询结果进行缓存(一级和二级);
2).cn.ganlixin.mapper.User命名空间,进行一次更新操作,此时user表的数据已经发生了变化,但是上面二级缓存的数据并没有更新,因为更新操作不是发生在cn.ganlixin.mapp.UserDep命名空间下(也就不会更新该空间下的缓存)。
这个时候一般建议使用redis分布式缓存。