zl程序教程

您现在的位置是:首页 >  后端

当前栏目

mybatis的缓存策略

mybatis缓存 策略
2023-09-14 08:56:52 时间

目录

一.mybatis缓存策略介绍

二.一级缓存

  2.1 一级缓存介绍

  2.2 一级缓存的注意事项

三.二级缓存

  3.1 二级缓存介绍

  3.2 二级缓存工作原理

  3.3 缓存优先级

  3.4 二级缓存的注意事项

 

一.mybatis缓存策略介绍

  先说普通的缓存,比如redis、memcache,使用这些缓存,是为了缓存“热点数据”,防止每次都从DB获取,或者防止每次都进行复杂开销大的计算(比如某个结果需要耗时5秒计算);

  mybatis是与数据库相关的,所以只是对从DB返回的数据进行缓存,防止每次都查询DB(可以缓解DB的一部分压力);

   

  mybatis中,有两种缓存策略,也就是一级缓存和二级缓存;在说这两种缓存的区别前,先简单提一下mybatis的几个关键对象:SqlSession可以认为是一个数据库会话(连接),由SqlSessionFactory创建,而SqlSessionFactory又是由SqlSessionFactoryBuilder创建。

  mybatis的一级缓存,缓存的数据,作用域范围是同一个SqlSession;

  mybatis的二级缓存,缓存的数据,作用域范围是同一个SqlSessionFactory创建的所有SqlSession。

  mybatis的缓存可以理解为一个map,map的key由namespace、statement-id、offset、limit、sql、env共同决定。

 

二.一级缓存

2.1 一级缓存介绍

  mybatis中,默认是开启一级缓存的

  一级缓存的作用域范围是同一个SqlSession,一级缓存的数据其实是存在SqlSession的Executor对象中,在Executor对象中使用一个HashMap来保存缓存。

  同一个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。

 

三.二级缓存

3.1 二级缓存介绍

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

  前面说过一级缓存是指同一个SqlSession的作用域,缓存的数据存在每一个SqlSession各自的空间中(准确的说是保存在每个SqlSession各自的Executor中)。

  而二级缓存的作用域,则是同一个SqlSessionFactory对象创建出的所有SqlSession,不同的SqlSessionFactory创建的SqlSession不能共享二级缓存(即使namespace、statement-id、offset、limit、sql、env都相同,也不能共享)。

 

3.2 二级缓存的原理

  二级缓存其实是使用CachingExecutor来包装一下Executor,使用的流程如下:

  1.每次执行Executor的时候,先在CachingExecutor的进行二级缓存的查询;

  2.如果二级缓存没有,则在进行一级缓存的查询,如果一级缓存有,则将一级缓存数据写入二级缓存;

  3.如果一级缓存没有,则进行数据库查询,然后写入查询的数据到一级缓存,然后再写入二级缓存;

  4.将查询结果返回。

  开启二级缓存后,同一个namespace下的所有sql的查询结果保存在一个cache中,原理图如下所示:

  

 

3.3 缓存优先级

  当开启二级缓存后,一级缓存和二级缓存就同时存在,这个时候,mybatis应该先查那个cache呢?

  答案是:二级缓存 > 一级缓存 > DB。

 

3.4 二级缓存的注意点

  1.一级缓存的数据写入二级缓存的前提:一个SqlSession调用close()之后,才会将一级缓存中的数据放到二级缓存中,如果不关闭SqlSession,数据只会存在于一级缓存中;

  2.二级缓存是基于namespace进行共享的,如果缓存多表联查的结果,那么二级缓存的数据可能不会及时更新,会有脏数据的风险。

  对于第二点,举个例子:

  1).进行一次查询,cn.ganlixin.mapper.UserDep命名空间,该空间连表查询user和dep表,将查询结果进行缓存(一级和二级);

  2).cn.ganlixin.mapper.User命名空间,进行一次更新操作,此时user表的数据已经发生了变化,但是上面二级缓存的数据并没有更新,因为更新操作不是发生在cn.ganlixin.mapp.UserDep命名空间下(也就不会更新该空间下的缓存)。

  这个时候一般建议使用redis分布式缓存。

 

  原文地址:https://www.cnblogs.com/-beyond/p/10130726.html