关于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的二级缓存和针对方法的缓存都生效了

posted @ 2018-04-27 01:02  一介書生  阅读(684)  评论(0)    收藏  举报