关于mybatis的一级缓存和二级缓存几点说明
原创内容,未经允许,不得转载

Mybatis在设计之初,考虑到了为查询构建缓存,设计了一级缓存和二级缓存.
Mybatis的一级缓存:
一级缓存是sqlSession.
Mybatis默认开启,并且不支持第三方缓存代替.
每次查询开启一个sqlSession,使用sql执行查询时,再次使用同一个查询条件执行相同的sql,不会开启数据库连接.
直接从sqlSession中拿.相信,如果你查找过相关资料都是对Mybatis进行如下解释.然而...
在使用SSM时,这种一级缓存带来的效果你真的体验过吗?
让我来做个测试,框架springboot+Mybatis
首先开启debug,打印sql语句
logging:
level:
com.magicabc.web.magicabcweb.mapper: debug
然后设计方法,重复调用sql
public User getUserById(String userphone) {
User user = null;
try {
user = userDao.getUserById(userphone);
System.out.println("1111111111############");
// System.out.println("缓存"+user.getUserphone());
user = userDao.getUserById(userphone);
System.out.println("222222222############");
} catch (Exception e) {
log.error(e.getMessage(),e);
}
return user;
}
用postman请求一次,结果:
2018-04-26 21:53:31.946 DEBUG 7344 --- [nio-8080-exec-5] c.m.w.m.mapper.IUserDao.getUserById : ==> Preparing: SELECT `userphone`, `username`, `password`, `headimagepath`, `sex`, `childAge`, `heartTime`, childName, grade, registerRationId, `hbintegral`, `dckintegral`, `jbnum` ,referrer,address, `isagent`, `activity`, `channel` , `openid`, `headurl`, `nickname`, `englishName`, `birthday`, `telephone`, `remark`, `relevanceCC`, `equipmentflag` FROM user WHERE userphone =? LIMIT 0, 1000 ; 2018-04-26 21:53:31.946 DEBUG 7344 --- [nio-8080-exec-5] c.m.w.m.mapper.IUserDao.getUserById : ==> Parameters: 18550444038(String) 2018-04-26 21:53:31.956 DEBUG 7344 --- [nio-8080-exec-5] c.m.w.m.mapper.IUserDao.getUserById : <== Total: 1 1111111111############ 2018-04-26 21:53:31.957 DEBUG 7344 --- [nio-8080-exec-5] c.m.w.m.mapper.IUserDao.getUserById : ==> Preparing: SELECT `userphone`, `username`, `password`, `headimagepath`, `sex`, `childAge`, `heartTime`, childName, grade, registerRationId, `hbintegral`, `dckintegral`, `jbnum` ,referrer,address, `isagent`, `activity`, `channel` , `openid`, `headurl`, `nickname`, `englishName`, `birthday`, `telephone`, `remark`, `relevanceCC`, `equipmentflag` FROM user WHERE userphone =? LIMIT 0, 1000 ; 2018-04-26 21:53:31.958 DEBUG 7344 --- [nio-8080-exec-5] c.m.w.m.mapper.IUserDao.getUserById : ==> Parameters: 18550444038(String) 2018-04-26 21:53:31.966 DEBUG 7344 --- [nio-8080-exec-5] c.m.w.m.mapper.IUserDao.getUserById : <== Total: 1 222222222############
发现同一个sql,执行了两次,说好的一级缓存呢?
原因在哪呢?
是因为Spring在中间搞鬼.Spring使用Mybatis执行查询有几个步骤:
1.创建sqlSession,绑定当前线程
2.执行查询
3.清除和当前线程绑定的sqlSessoin,释放资源
Spring为了降低连接sqlSession的资源消耗搞的事情让mybatis一级缓存失效了...
Mybatis的二级缓存:
以namespace为单位,多个sqlSession共享一个nameSpace.
在springboot中如何开启Mybatis的二级缓存呢?
第一步:pom.xml引入相关包
<dependency> <groupId>org.mybatis.caches</groupId> <artifactId>mybatis-ehcache</artifactId> <version>1.1.0</version> </dependency>
第二步:配置缓存存活时间 ehcache.xml
<?xml version="1.0" encoding="UTF-8"?> <ehcache> <defaultCache maxElementsInMemory="1000" timeToLiveSeconds="10" timeToIdleSeconds="10" /> </ehcache>
第三步:配置XXXMapper.xml
<cache readOnly="true" type="org.mybatis.caches.ehcache.EhcacheCache"/>
如果不想让某个seqSession使用二级缓存,使用useCache="false"
<select id="getUserById" parameterType="java.lang.String" resultMap="userResultMap" useCache="false">
默认情况下,useCache是true.在接下里测试中我们让这个查询使用缓存.
第四步:测试
2018-04-27 00:11:54.085 DEBUG 7708 --- [nio-8080-exec-3] c.m.web.magicabcweb.mapper.IUserDao : Cache Hit Ratio [com.magicabc.web.magicabcweb.mapper.IUserDao]: 0.9152542372881356 2018-04-27 00:11:54.090 DEBUG 7708 --- [nio-8080-exec-3] c.m.w.m.mapper.IUserDao.getUserById : ==> Preparing: SELECT `userphone`, `username`, `password`, `headimagepath`, `sex`, `childAge`, `heartTime`, childName, grade, registerRationId, `hbintegral`, `dckintegral`, `jbnum` ,referrer,address, `isagent`, `activity`, `channel` , `openid`, `headurl`, `nickname`, `englishName`, `birthday`, `telephone`, `remark`, `relevanceCC`, `equipmentflag` FROM user WHERE userphone =? LIMIT 0, 1000 ; 2018-04-27 00:11:54.090 DEBUG 7708 --- [nio-8080-exec-3] c.m.w.m.mapper.IUserDao.getUserById : ==> Parameters: 18550444038(String) 2018-04-27 00:11:54.097 DEBUG 7708 --- [nio-8080-exec-3] c.m.w.m.mapper.IUserDao.getUserById : <== Total: 1 1111111111############ 2018-04-27 00:11:54.097 DEBUG 7708 --- [nio-8080-exec-3] c.m.web.magicabcweb.mapper.IUserDao : Cache Hit Ratio [com.magicabc.web.magicabcweb.mapper.IUserDao]: 0.9166666666666666 222222222############ 2018-04-27 00:11:58.540 DEBUG 7708 --- [nio-8080-exec-4] c.m.web.magicabcweb.mapper.IUserDao : Cache Hit Ratio [com.magicabc.web.magicabcweb.mapper.IUserDao]: 0.9180327868852459 1111111111############ 2018-04-27 00:11:58.540 DEBUG 7708 --- [nio-8080-exec-4] c.m.web.magicabcweb.mapper.IUserDao : Cache Hit Ratio [com.magicabc.web.magicabcweb.mapper.IUserDao]: 0.9193548387096774 222222222############ 2018-04-27 00:12:13.934 DEBUG 7708 --- [nio-8080-exec-5] c.m.web.magicabcweb.mapper.IUserDao : Cache Hit Ratio [com.magicabc.web.magicabcweb.mapper.IUserDao]: 0.9047619047619048 2018-04-27 00:12:13.940 DEBUG 7708 --- [nio-8080-exec-5] c.m.w.m.mapper.IUserDao.getUserById : ==> Preparing: SELECT `userphone`, `username`, `password`, `headimagepath`, `sex`, `childAge`, `heartTime`, childName, grade, registerRationId, `hbintegral`, `dckintegral`, `jbnum` ,referrer,address, `isagent`, `activity`, `channel` , `openid`, `headurl`, `nickname`, `englishName`, `birthday`, `telephone`, `remark`, `relevanceCC`, `equipmentflag` FROM user WHERE userphone =? LIMIT 0, 1000 ; 2018-04-27 00:12:13.940 DEBUG 7708 --- [nio-8080-exec-5] c.m.w.m.mapper.IUserDao.getUserById : ==> Parameters: 18550444038(String) 2018-04-27 00:12:13.946 DEBUG 7708 --- [nio-8080-exec-5] c.m.w.m.mapper.IUserDao.getUserById : <== Total: 1 1111111111############ 2018-04-27 00:12:13.946 DEBUG 7708 --- [nio-8080-exec-5] c.m.web.magicabcweb.mapper.IUserDao : Cache Hit Ratio [com.magicabc.web.magicabcweb.mapper.IUserDao]: 0.90625 222222222############
当间隔不足10秒时,再次查询直接从缓存拿,当间隔超过10秒,再次使用同样条件查询,不走缓存,查询数据库.
网上有很多案例,说使用mybatis的二级缓存,还需要在配置文件中配置
##开启mybatis的二级缓存
# configuration:
# cache-enabled: true
然后在启动类中添加@EnableCaching注解开启对缓存的支持
经过测试,上述两个步骤对实现Mybatis的二级缓存来说不是必须的.没有这两个设置,对结果也没有影响.
关于@EnableCaching的讨论
在启动类中添加@EnableCaching注解的作用是针对在代码中有使用@Cacheable @CachePut @CacheEvict 来说的
例如我们要对一个service方法进行缓存:
第一步:导包
<!--springboot对cache的支持 不适用这个,无法对cache进行有效控制--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-cache</artifactId> </dependency> <!--第三方ehCache ehCache--> <dependency> <groupId>net.sf.ehcache</groupId> <artifactId>ehcache-core</artifactId> <version>2.5.3</version> </dependency>
第二步:启动类开启@EnableCaching
@SpringBootApplication
@EnableCaching
public class MagicabcwebApplication{
public static void main(String[] args) {
SpringApplication.run(MagicabcwebApplication.class, args);
}
}
第三步:配置自定义ehcache.xml
<?xml version="1.0" encoding="UTF-8"?> <ehcache> <cache name="user" maxElementsInMemory="1000" timeToLiveSeconds="5" timeToIdleSeconds="10" /> </ehcache>
第四步:对方法进行缓存
@Cacheable(value = "user",key = "#userphone")//缓存之后不清理,不再执行方法
// @CachePut(value = "user",key = "#userphone") //只对结果进行缓存,方法该访问访问,该查数据库查数据库
// @CacheEvict(value = "user",key = "#userphone")//删除缓存
public User getUserById(String userphone) {
User user = null;
try {
user = userDao.getUserById(userphone);
System.out.println("1111111111############");
// System.out.println("缓存"+user.getUserphone());
user = userDao.getUserById(userphone);
System.out.println("222222222############");
} catch (Exception e) {
log.error(e.getMessage(),e);
}
return user;
}
第五步:测试
结果:
2018-04-27 00:54:23.757 DEBUG 4956 --- [io-8080-exec-10] c.m.w.m.mapper.IUserDao.getUserById : ==> Preparing: SELECT `userphone`, `username`, `password`, `headimagepath`, `sex`, `childAge`, `heartTime`, childName, grade, registerRationId, `hbintegral`, `dckintegral`, `jbnum` ,referrer,address, `isagent`, `activity`, `channel` , `openid`, `headurl`, `nickname`, `englishName`, `birthday`, `telephone`, `remark`, `relevanceCC`, `equipmentflag` FROM user WHERE userphone =? LIMIT 0, 1000 ; 2018-04-27 00:54:23.757 DEBUG 4956 --- [io-8080-exec-10] c.m.w.m.mapper.IUserDao.getUserById : ==> Parameters: 18550444038(String) 2018-04-27 00:54:23.763 DEBUG 4956 --- [io-8080-exec-10] c.m.w.m.mapper.IUserDao.getUserById : <== Total: 1 1111111111############ 2018-04-27 00:54:23.763 DEBUG 4956 --- [io-8080-exec-10] c.m.w.m.mapper.IUserDao.getUserById : ==> Preparing: SELECT `userphone`, `username`, `password`, `headimagepath`, `sex`, `childAge`, `heartTime`, childName, grade, registerRationId, `hbintegral`, `dckintegral`, `jbnum` ,referrer,address, `isagent`, `activity`, `channel` , `openid`, `headurl`, `nickname`, `englishName`, `birthday`, `telephone`, `remark`, `relevanceCC`, `equipmentflag` FROM user WHERE userphone =? LIMIT 0, 1000 ; 2018-04-27 00:54:23.763 DEBUG 4956 --- [io-8080-exec-10] c.m.w.m.mapper.IUserDao.getUserById : ==> Parameters: 18550444038(String) 2018-04-27 00:54:23.769 DEBUG 4956 --- [io-8080-exec-10] c.m.w.m.mapper.IUserDao.getUserById : <== Total: 1 222222222############ 2018-04-27 00:55:11.304 DEBUG 4956 --- [nio-8080-exec-3] c.m.w.m.mapper.IUserDao.getUserById : ==> Preparing: SELECT `userphone`, `username`, `password`, `headimagepath`, `sex`, `childAge`, `heartTime`, childName, grade, registerRationId, `hbintegral`, `dckintegral`, `jbnum` ,referrer,address, `isagent`, `activity`, `channel` , `openid`, `headurl`, `nickname`, `englishName`, `birthday`, `telephone`, `remark`, `relevanceCC`, `equipmentflag` FROM user WHERE userphone =? LIMIT 0, 1000 ; 2018-04-27 00:55:11.304 DEBUG 4956 --- [nio-8080-exec-3] c.m.w.m.mapper.IUserDao.getUserById : ==> Parameters: 18550444038(String) 2018-04-27 00:55:11.309 DEBUG 4956 --- [nio-8080-exec-3] c.m.w.m.mapper.IUserDao.getUserById : <== Total: 1 1111111111############ 2018-04-27 00:55:11.310 DEBUG 4956 --- [nio-8080-exec-3] c.m.w.m.mapper.IUserDao.getUserById : ==> Preparing: SELECT `userphone`, `username`, `password`, `headimagepath`, `sex`, `childAge`, `heartTime`, childName, grade, registerRationId, `hbintegral`, `dckintegral`, `jbnum` ,referrer,address, `isagent`, `activity`, `channel` , `openid`, `headurl`, `nickname`, `englishName`, `birthday`, `telephone`, `remark`, `relevanceCC`, `equipmentflag` FROM user WHERE userphone =? LIMIT 0, 1000 ; 2018-04-27 00:55:11.310 DEBUG 4956 --- [nio-8080-exec-3] c.m.w.m.mapper.IUserDao.getUserById : ==> Parameters: 18550444038(String) 2018-04-27 00:55:11.315 DEBUG 4956 --- [nio-8080-exec-3] c.m.w.m.mapper.IUserDao.getUserById : <== Total: 1 222222222############
同样的参数,对这个方法10秒内只能执行一次,下一次结果直接从缓存中拿.但是每一个Mybatis的查询都执行了两次.Mybatis的二级缓存没有生效.
10秒后,再次访问,就会从数据库中再次拿数据,因为缓存被清除了.
扩展
如果同时用缓存控制住方法和mybatis的二级缓存,只需要将ehcache.xml中修改为:
<?xml version="1.0" encoding="UTF-8"?> <ehcache> <cache name="user" maxElementsInMemory="1000" timeToLiveSeconds="5" timeToIdleSeconds="10" /> <defaultCache maxElementsInMemory="1000" timeToLiveSeconds="10" timeToIdleSeconds="10" /> </ehcache>
然后修改XXXMapper.xml
<cache readOnly="true" type="org.mybatis.caches.ehcache.EhcacheCache"/>
再次使用同样的参数执行这个getUserById方法,控制台打印如下:
2018-04-27 00:58:04.632 INFO 16972 --- [nio-8080-exec-2] o.a.c.c.C.[.[localhost].[/magicabc.web] : Initializing Spring FrameworkServlet 'dispatcherServlet' 2018-04-27 00:58:04.632 INFO 16972 --- [nio-8080-exec-2] o.s.web.servlet.DispatcherServlet : FrameworkServlet 'dispatcherServlet': initialization started 2018-04-27 00:58:04.648 INFO 16972 --- [nio-8080-exec-2] o.s.web.servlet.DispatcherServlet : FrameworkServlet 'dispatcherServlet': initialization completed in 16 ms 2018-04-27 00:58:04.690 DEBUG 16972 --- [nio-8080-exec-2] c.m.web.magicabcweb.mapper.IUserDao : Cache Hit Ratio [com.magicabc.web.magicabcweb.mapper.IUserDao]: 0.0 2018-04-27 00:58:04.694 INFO 16972 --- [nio-8080-exec-2] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting... 2018-04-27 00:58:04.874 INFO 16972 --- [nio-8080-exec-2] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed. 2018-04-27 00:58:04.878 DEBUG 16972 --- [nio-8080-exec-2] c.m.w.m.mapper.IUserDao.getUserById : ==> Preparing: SELECT `userphone`, `username`, `password`, `headimagepath`, `sex`, `childAge`, `heartTime`, childName, grade, registerRationId, `hbintegral`, `dckintegral`, `jbnum` ,referrer,address, `isagent`, `activity`, `channel` , `openid`, `headurl`, `nickname`, `englishName`, `birthday`, `telephone`, `remark`, `relevanceCC`, `equipmentflag` FROM user WHERE userphone =? LIMIT 0, 1000 ; 2018-04-27 00:58:04.892 DEBUG 16972 --- [nio-8080-exec-2] c.m.w.m.mapper.IUserDao.getUserById : ==> Parameters: 18550444038(String) 2018-04-27 00:58:04.908 DEBUG 16972 --- [nio-8080-exec-2] c.m.w.m.mapper.IUserDao.getUserById : <== Total: 1 1111111111############ 2018-04-27 00:58:04.911 DEBUG 16972 --- [nio-8080-exec-2] c.m.web.magicabcweb.mapper.IUserDao : Cache Hit Ratio [com.magicabc.web.magicabcweb.mapper.IUserDao]: 0.5 222222222############ 2018-04-27 00:58:10.012 DEBUG 16972 --- [nio-8080-exec-3] c.m.web.magicabcweb.mapper.IUserDao : Cache Hit Ratio [com.magicabc.web.magicabcweb.mapper.IUserDao]: 0.6666666666666666 1111111111############ 2018-04-27 00:58:10.012 DEBUG 16972 --- [nio-8080-exec-3] c.m.web.magicabcweb.mapper.IUserDao : Cache Hit Ratio [com.magicabc.web.magicabcweb.mapper.IUserDao]: 0.75 222222222############ 2018-04-27 01:01:36.607 DEBUG 16972 --- [nio-8080-exec-6] c.m.web.magicabcweb.mapper.IUserDao : Cache Hit Ratio [com.magicabc.web.magicabcweb.mapper.IUserDao]: 0.6 2018-04-27 01:01:36.613 DEBUG 16972 --- [nio-8080-exec-6] c.m.w.m.mapper.IUserDao.getUserById : ==> Preparing: SELECT `userphone`, `username`, `password`, `headimagepath`, `sex`, `childAge`, `heartTime`, childName, grade, registerRationId, `hbintegral`, `dckintegral`, `jbnum` ,referrer,address, `isagent`, `activity`, `channel` , `openid`, `headurl`, `nickname`, `englishName`, `birthday`, `telephone`, `remark`, `relevanceCC`, `equipmentflag` FROM user WHERE userphone =? LIMIT 0, 1000 ; 2018-04-27 01:01:36.614 DEBUG 16972 --- [nio-8080-exec-6] c.m.w.m.mapper.IUserDao.getUserById : ==> Parameters: 18550444038(String) 2018-04-27 01:01:36.619 DEBUG 16972 --- [nio-8080-exec-6] c.m.w.m.mapper.IUserDao.getUserById : <== Total: 1 1111111111############ 2018-04-27 01:01:36.620 DEBUG 16972 --- [nio-8080-exec-6] c.m.web.magicabcweb.mapper.IUserDao : Cache Hit Ratio [com.magicabc.web.magicabcweb.mapper.IUserDao]: 0.6666666666666666 222222222############
可以观察到,针对mybatis的二级缓存和针对方法的缓存都生效了

浙公网安备 33010602011771号