您当前的位置:网站首页>随缘居,SpringBoot 下 Mybatis 的缓存,放开那三国2

随缘居,SpringBoot 下 Mybatis 的缓存,放开那三国2

2019-04-20 01:30:59 投稿作者:admin 围观人数:139 评论人数:0次

“IT魔幻屋”致力于让你遇见随缘居,SpringBoot 下 Mybatis 的缓存,铺开那三国2更好的自己!

说起 mybatis,作为 Java 程序员应该是无人不知,它是常用的数据库拜访结构。与 Spring 和 Struts 组成了 Java Web 开发的三剑客— SSM。当然跟着 Spring Boot 的开展,现在越来越多的企业选用的是 SpringBoot + mybatis 的形式开发,咱们公司也不破例。而 mybatis 关于我也只是停留在会用罢了,没想过怎样去了解它,更不知道它的缓存机制了,直到那个存亡难忘的 BUG。故事的布景比较长,但并不是烦琐,只是让读者知道这个 BUG 触发的场景,加深回忆。在遇到类似问题时,能够敏捷定位。


先说下故事的条件,为了防止用户在动态中输入特别字符,用户的动态都是编码后发到后台,而后台在存入到 DB 表之前会解码以方便在 DB 中检查以及上签到搜索引擎。在查询用户动态的时分先从 DB 表中读取并在后台做一次编码再传到前端,前端再解码就能够正常展现了。流程如下图:

有一天后端预发环境发布结束后,用户的动态页面有的动态显示正常,而有的却是被编码过的。看到现象后的第一个反响便是有问题的动态被编码了两次,可是编码操作只会在 service 层的 findById 中有。理论不会在上层犯这种初级过错。话不多说便开端排查新添加的代码,发现只需进入了新添加代码中的某个 if 分支则被编码了两次。分支中除了再次调用 findById(必要性不评论),也无其他特别代码了。百思不得其解后请教了周围的老司机,老司机说可能是 mybatis 缓存。所以看了下我代码,将编码的操作从 findById 中移出来后再次发布到预发,正常了,心想老司机不愧是老司机。本次 BUG 触发的有两个条件需求留意:

  • 整个操作过程都在一个函数中,而函数上面加了 @Transactional 的注解(对 mybatis 来说是在同一个 SESSION 中)
  • 一般只会调用 findByIdy 一次,假如进入分支则会调用两次 (第一次调用后做了编码后被缓存,第2次从缓存读后持续被编码)

便开端谷歌 m伯妮丝ybatis 的缓存机制,搜到了一篇十分不错的文章《 聊聊 mybatis 的缓存机制 》,引荐我们看一下。可是这篇文章讲到了源码,触及的比较深。并且并没讲 SpringBoot 下 mybatis 下的缓存知识点,遂作此篇,以作弥补。

缓存的装备

SpringBoot + mybatis 环境建立很简单并且网上一堆教程,这儿不布鼓雷门了,记住在项目中将 mytatis 的源码下载下来即可。mybaits 一共有两级缓存:一级缓存的装备 key 是 localCacheScope,而二级缓存的装备 key 是 cacheEnabled,从姓名上能够得出以下信息:

  • 一级缓存是本地或许说部分缓存,它不能被封闭,只能装备缓存规模。SESSION 或许 STATEMENT。

  • 二级缓存才是 mybatis 的正统,功用会更强大些。

先来看下在 SpringBoot中 怎么装备 mybatis 缓存的相关信息。默许状况下 SpringBoot 下的 mybatis 一级缓存为 SESSION 等级,二级缓存也是翻开的,能够在 mybatis 源码中的 org.apache.ibatis.session.Configuration.class 文件中看到气候15天(idea中翻开),如下图:

也能够经过以下测验程序检查缓存敞开状况:

@RunWith(SpringRunner.class)
@SpringBootTest
public class LearnApplicationTests {
private SqlSessionFactory factory;
@Before
public void cpa考试setUp() throws Exception {
InputStream inputStream = Resources.getResourceAsStream("mybatis/mybatis-config.xml");
factory = new SqlSessionFactoryBuilder().build(inputStream);
}
@Test
public void show脸色发黄是什么原因DefaultCacheConfiguration() {
System.out.println("一级缓存规模: " +一的成语 factory.getConfiguration().getLocalCacheScope());
System.out.println("二级缓存是否被启用: " + factory.getConfiguration().isCacheEnabled());
}
}

假如要设置一级缓存的缓存等级和开关二级缓存,在 mybatis-config.xml (当然也能够在 application.xml/yml 中装备)参加如下装备即可:






但需求留意的是二级缓存 cacheEnabled 只是个总开关,如随缘居,SpringBoot 下 Mybatis 的缓存,铺开那三国2果要让二级缓存真深山野夫正收效还需求在 mapper xml 文件中参加 。一级缓存只在同一 SESSION 或许 STATEMENT 之间同享,二级缓存能够跨 SESSION,敞开后它们默许具有如下特性:

  • 映射文件中所有的 select 句子将被缓存
  • 映射文件中所有的 insert/update/delete 句子将改写缓存

一二级缓存一起敞开的状况下,数据的查询次序是 二级缓存 -> 一级缓存 -> 数据库。一级缓存比较简单,而二级缓存能够设置更多的特点,只需求在 mapper 的 xml 文件中的 中装备即可,详细如下:

 type = "org.mybatis.caches.ehcachedota2国服.安仔栋笃笑LoggingEhcache" //指定运用的缓存类,mybatis默许运用HashMap进行缓存,能够指定第三方缓存
eviction = "LRU" //默许是 LRU 筛选缓存的算法,有如下几种:
//1.LRU – 最虫师近最少运用的:移除最长时刻不被运用的目标。
//2.FIFO – 先进先出:按目标进入缓存的次序来麻辣香锅的做法移除它们。
//3.SOFT – 软引证:移除根据废物收回器状况和软引证规矩的目标。
//4.WEAK – 弱引证:更积极地移除根据废物收集器状况和弱引证规矩的目标
flushInterval = "1000" //清空缓存的时刻距离,单位毫秒,能够被设置为恣意的正整数。 默许状况是不设置,也便是没有改写距离,缓存只是调用句子时改写。
size = "100" //缓存目标的个数,恣意正整数,默许值是1024。
readOnly = "true" //缓存是否随缘居,SpringBoot 下 Mybatis 的缓存,铺开那三国2只读,进步读取功率
blocking = "true" //是否运用堵塞缓存,默许为false,当指定为true时将选用BlockingCache进行封装,blocking,
//堵塞的意思,运用BlockingCache会在查询缓存时锁住对应的Key,假如缓存射中了则会开释对应的锁,
//不然会在查询数据库今后再开释锁这样能够阻挠并发状况下多个线程一起查询数据,概况可参阅BlockingCache的源码。
/>

触发缓存

  1. 装备一级缓存为 SESSION 等级

Controller 中调用两次 getOne,代码如下:

@RequestMapping("/getUser")
public UserEntity getUser(Long id) {
//第一次调用
UserEntity user1=userMapper.getOne(id);
//第2次调用
UserEntity user2=userMapper.getOne(id);
return user1;
}

调用: http://localhost:8080/getUser?id=1,打印成果如下:

从图中的 1/2/3/4 能够看出每次 mapper 层的一次接口调用如 getOne 就会创立一个 session,并且在履行结束后封闭 session。所以两次调用并不在一个 session 中,一级缓存并没有随缘居,SpringBoot 下 Mybatis 的缓存,铺开那三国2发作作用。敞开业务,Controller 层随缘居,SpringBoot 下 Mybatis 的缓存,铺开那三国2代码如下:

@RequestMapping("/getUser")
@Transactional(rollbackFor = Throwable.class)
public UserEntity getUser(Long id) {
//第一次调用
UserEntity user1=userMapper.getOne(id);
//第2次调用
UserEntity user2=userMapper.getOne(id);
return user1;
}

因为在同一个业务中,尽管调用了 sesystemlect 操作两次可是只履行了一次 sql ,缓存发挥了作用。这就跟一开端我遇到的那个 BUG 场景相同:同一随缘居,SpringBoot 下 Mybatis 的缓存,铺开那三国2 session 且 select 调用 > 1 次。假如在两次调用中心刺进 update 操作,缓存会当即失效。只需 session 中有 in二炮手电视剧全集sert、update 和 delete 句子,该 session 中的缓存会当即被改写。可是留意这只是在同一 session 之间。不同 session 之间如 session1 和 session2,session1 里的 insert/update/delete 并不会影响 session 2 下的缓存,这在高并发或许分布式的状况下会发生脏数据。所以主张将一级缓存等级调成 statement。

  1. 装备一级缓存为 STATEMENT 等级

再次将(1)中的无业务和有业务的代码别离履行一遍,打印成果一直如下:

装备成 SATEMENT 后,一级缓存相当于被封闭了。STATEMENT 等级暂时欠好模仿,可是我猜想 STATEMENT 等级即在同一履行 sql 的接口中(如上面的 getOne 中)缓存,出了 getOne 缓存即失效。

  1. 装备二级缓存,一起为了防止一级缓存的搅扰,将一级缓存设置为 STATEMENT

Controller 中去掉 @Transactional 注解代码如下:

@RequestMapping("/getUser")
public UserEntity getUser(Long id) {
UserEn鹅的做法tity user1=userMapper.getOne(id);
UserEntity user2=userMapper.getOne(id);
return user1;
}

当然二级缓存开关确保翻开,在 mapper xml 文件中参加 ,整个文件代码如下:










id, name, sex





履行 http://localhost:8080/getUser?id=1,打印成果如下:

从图中红框能够看出第2次查询射中缓存,0.5 是射中率。再次履行

http://随缘居,SpringBoot 下 Mybatis 的缓存,铺开那三国2localhost:8080/getUser?id=1

打印成果如下:

这次一次 sql 也没履行了,缓存射中率上升到 0.75了,所以说二级缓存大局缓存。但它的缓存规模也是有限的,一级缓存在同一个 session 中。二级缓存尽管能够跨 session 但也只能在同一 namespace 中,所谓 na盛世军婚mespace 即 mapper xml 文件。详细试验请看《聊聊 mybatis 的缓存机制》中的关于二级缓存的试验 4 和 5。再看下二级缓存装备对二级缓存的影响,为了显着的看出作用,只改如下装备:

 size="1" //一次只白云边能缓存一个目标
flushInterval="5000" //改写时刻为 5s
/>
/****/
@RequestMapping("/getUser")
public UserEntity get钟庆厚User(Long id, Long id2) {
//第一个目标 1
System.out.println("================缓存目标 青楼文娱攻略1=================");
UserEntity user1 = userMapper.getOne(id);
//另一个目标 2
System.out.println("========缓存目标 2,除掉缓存中的目标 1=======");
UserEntity user2=userMa净化号舰船pper.getOne(id2);
user2 = userMapper.getOne(id2);
//再次读取第一个目标
System.out.println("=======艺人张晞===缓存被除掉,履行查询 sql===========");
user1 = userMapper.getOne(id);
//暂停 5s
try {
sleep(5000);
}catch (Exception e){
e.printStackTrace();
}
System.out.println("============5s 后再次查询目标 2=============");
user2 = userMapper.getOne(id2);
return user1;
}

履行 http://localhost:8080/getUser?id=1&id2=2 最终打印的成果如下:

能够看出二级缓存只能缓存一个目标且 5s 后就失效了,装备收效。缓存装备中还有一个重要的装备 type,该装备能够装备第三方的 cache,特别在高并发和分布式状况下。当然,运用更专业的分布式缓存才是王道,例如 redis 等。

重视“IT魔幻屋”,学习更多前沿技术,来这儿你将遇见更好的自己!

the end
各种卧室布置方案,设计师的世界