Mybatis内置了弱小的事务性查问缓存机制,正确应用Mybatis的缓存机制能够无效进步利用的性能。因为个别状况下咱们利用的大部分性能耗费都和数据库查问无关,如果可能无效命中缓存、适当防止或缩小与数据库的交互,肯定是改善利用性能的不二抉择。
然而缓存机制是一把双刃剑,不失当的应用缓存可能会导致数据不统一的问题。
决不能为了解决性能问题而引入数据不统一问题,这是每个开发人员都应该具备的一个根本意识。
所以,为了既可能优雅的解决性能问题,又可能平安的躲避数据不统一问题。咱们有必要对Mybatis的缓存机制做一个透彻的理解。
意识Mybatis缓存机制
Mybatis提供了两种不同的缓存机制,也就是咱们常常说的一级缓存、二级缓存。
一级缓存:也叫LocalCache,是基于SqlSession的缓存,也就是说缓存是存储在SqlSession这个级别的,会随着SqlSession的敞开而隐没。
二级缓存:是跨SqlSession生命周期的,然而Mybatis的二级缓存的作用于是定义在namespace级别的,对应着mapper.xml文件的定义。跨作用于失效须要进行专门的定义。
其实程序员应该认真对待的是Mybatis的二级缓存机制,因为以及缓存机制是默认开启的、简直也不存在数据不统一的问题,二级缓存机制是须要通过配置开启、而且用不好很容易导致数据不统一的问题。
而且,一级缓存应用起来应该非常简单,所以Mybatis官网对于以及缓存的介绍简直没有,而官网无关缓存的阐明文字简直通篇都是介绍二级缓存的(参考https://mybatis.org/mybatis-3...)。
倡议大家对官网文档做认真学习钻研,对于官网曾经交代过的内容,咱们就不再啰嗦了,本篇文章次要补充一些官网文章没有提及的无关Mybatis缓存机制的技术细节。
一级缓存
以及缓存是默认开启的,咱们不须要做任何配置就能够应用Mybatis的一级缓存。
如果你不想开启Mybatis的一级缓存,能够通过Settings配置做全局敞开(localCacheScope=STATEMENT)。
如果你只是针对某一句sql不想开启以及缓存,则能够在mapping的xml文件中针对该sql语句配置flushCache为true,留神flushCache对以及缓存和二级缓存都失效。
倡议还是采纳Mybatis的默认配置,开启一级缓存,尤其是对于那些有高级程序员参加的我的项目,他们对性能的问题思考的可能会少一点,在一个交易中对同一条数据可能会屡次查问,这个时候咱们当然能够通过框架设计、培训、code review等办法尽可能防止这种状况,然而如果无奈防止的话,起码Mybatis的一级缓存能够帮忙咱们在肯定水平上防止无谓的数据库申请。
二级缓存#开启
官网说的十分明确,二级缓存须要须要在mapping配置文件中手动开启,否则Mybatis不会主动开启二级缓存。
摘抄官网的一段话:
MyBatis 内置了一个弱小的事务性查问缓存机制,它能够十分不便地配置和定制。 为了使它更加弱小而且易于配置,咱们对 MyBatis 3 中的缓存实现进行了许多改良。
默认状况下,只启用了本地的会话缓存,它仅仅对一个会话中的数据进行缓存。 要启用全局的二级缓存,只须要在你的 SQL 映射文件中增加一行:
<cache/>
基本上就是这样。这个简略语句的成果如下:
- 映射语句文件中的所有 select 语句的后果将会被缓存。
- 映射语句文件中的所有 insert、update 和 delete 语句会刷新缓存。
- 缓存会应用最近起码应用算法(LRU, Least Recently Used)算法来革除不须要的缓存。
- 缓存不会定时进行刷新(也就是说,没有刷新距离)。
- 缓存会保留列表或对象(无论查询方法返回哪种)的 1024 个援用。
- 缓存会被视为读/写缓存,这意味着获取到的对象并不是共享的,能够平安地被调用者批改,而不烦扰其余调用者或线程所做的潜在批改。
不开启二级缓存的状况下,Mybatis底层用BaseExecutor对象执行sql,开启后应用CachingExecutor执行sql,CachingExecutor在创立的时候会持有Mapper初始化过程中以装璜器模式创立好的层层包装的SynchronizedCache对象,sql语句的执行后果最终由这个包装器持有从而实现缓存。
二级缓存#作用范畴
咱们后面曾经说过了二级缓存的作用范畴是namespace,Mybatis官网也有一个与句话的提醒:
提醒 缓存只作用于 cache 标签所在的映射文件中的语句。如果你混合应用 Java API 和 XML 映射文件,在共用接口中的语句将不会被默认缓存。你须要应用 @CacheNamespaceRef 注解指定缓存作用域。
咱们肯定要对这句话有足够的器重,因为这个中央如果使用不当的话,也是容易引起缓存一致性问题的。
咱们后面开篇也说过数据一致性问题,这个做一个简略的解释:
引入缓存的目标是为了尽可能减少数据库I/O从而进步零碎性能的,如果第一次查问从数据库中获取到了id=1的用户数据并缓存起来,第二次再执行该查问获取id=1的用户数据饿时候,Mybatis就会查看缓存中是否曾经有该数据,如果有的话Mybatis就不会再次执行数据库查问了,会间接给申请端返回缓存中的数据。这个时候咱们也常常称为缓存命中。
如果在二次查问之间有操作批改了id=1的用户信息怎么办,如果Mybatis不晓得本次批改而间接返回了缓存数据的话,就会导致数据不统一的问题,因为利用给前端返回了不正确的数据。
Mybatis当然有本人的解决方案,这个解决方案就是在更新数据的时候刷新缓存,刷新缓存其实就是清空缓存,那么在批改之后的首次查问就会因为无奈命中缓存而通过数据库查问来获取数据,这样的话就不会导致数据不统一的问题了。
然而现实情况往往会比你设想中的简单,比如说你的用户查问的sql语句定义在mapperA.xml中,而用户更新的sql语句定义在mapperB.xml中。
这种状况下,喜剧就产生了,用户更新后基本就不会触发用户信息查问的缓存刷新,因为他们两个作用域不一样,基本不在一个世界中。从技术角度来看,Mybatis底层在进行缓存的时候是以namespace或者说是mapper.xml文件为根底创立缓存对象的,上述的用户查问语句和用户更新语句的缓存对象不同,所以用户更新语句执行实现后基本就不会刷新用户查问语句所在的那个缓存对象!
咱们在开发过程中肯定要留神这个问题,否则的话你可能只晓得我的项目中有些奇怪的问题是Mybatis二级缓存机制导致的,然而却不晓得具体的底层起因,出问题之后要么就是关掉二级缓存,要么就是一通胡乱配置,flushCache或者userCache,这样有时候可能碰巧解决了问题,然而对于底层的技术原理还是没有把握,对集体来说也没有什么帮忙。
二级缓存#刷新距离
通过参数flushInterval配置,比方:
<cache
eviction="FIFO"
flushInterval="60000"
size="512"
readOnly="true"/>
设置Mybatis二级缓存刷新距离为60秒,意思是两次查问工夫距离如果超过60秒的话,缓存将会被刷新。
倡议这个参数要么不做配置,Mybatis默认不刷新缓存,要么就配置大一点,比方3小时。
上一篇 MybatisL拦截器