背景

mybatis提供了缓存功能,但是如果配置不当就会出现脏数据的问题,因此需要了解mybatis缓存的原理以及如何对缓存进行设置。

基本概念

mybatis中提供了一级缓存和二级缓存,一级缓存是 SqlSession 级别的,也即同一个 SqlSession 实例执行的SQL语句会使用同一份缓存。(至于 SqlSession 是一个线程一个实例,还是一个Http请求一个实例,则要看项目的设置,Spring中 <font style="color:#F5222D;">SqlSession</font> 级别待补充

一级缓存缺点:

  1. 只要 SqlSession 执行了一次 update 操作,就会clear一级缓存,不论 updateselect 是否是同一个 namespace 。因此一级缓存只有在连续 select 的情况下才会命中,命中的概率不高。
  2. <font style="color:#8C8C8C;">SqlSession1</font> 执行了 <font style="color:#8C8C8C;">select</font> <font style="color:#8C8C8C;">SqlSession2</font> 执行了 <font style="color:#8C8C8C;">update</font> <font style="color:#8C8C8C;">SqlSession1</font> 再次执行 <font style="color:#8C8C8C;">select</font> ,虽然数据库中的数据已经被SqlSession2修改,但SqlSession1依然可以命中一级缓存,造成脏数据。

为了解决一级缓存的缺点,便有了 namespace 级别的二级缓存,只有同一个namespace 下的 updateselect 才会互相影响,同时解决了两个 SqlSession 操作同一份数据导致的脏数据问题。

一级缓存

配置项

可以通过mybatis配置文件中的 settings 对一级缓存进行配置。

设置 参数描述 有效值 默认值
localCacheScope MyBatis 利用本地缓存机制(Local Cache)防止循环引用(circular references)和加速重复嵌套查询。 默认值为 SESSION,这种情况下会缓存一个会话中执行的所有查询。 若设置值为 STATEMENT,本地会话仅用在语句执行上,对相同 SqlSession 的不同调用将不会共享数据。 SESSION STATEMENT SESSION

localCacheScopeSTATEMENT 时,每执行一个SQL语句就会将一级缓存清空,相当于关闭一级缓存。

实现原理

一级缓存实现类是 BaseExecutor 中的关联的一个 PerpetualCachePerpetualCache 内部使用 HashMap 存放缓存,由于一个 SqlSession 关联一个 Executor ,所以一个 SqlSession 关联一个 PerpetualCache ,也即一级缓存是 SqlSession 级别的。

二级缓存

配置

可以通过mybatis配置文件中的 settings 对二级缓存进行配置。

设置 参数描述 有效值 默认值
cacheEnabled 全局地开启或关闭配置文件中的所有映射器已经配置的任何缓存。 true false true

也可以通过Mapper XML文件中的 cachecache-ref 进行配置

  • cache – 给定命名空间的缓存配置。
  • cache-ref – 其他命名空间缓存配置的引用。
  1. cacheEnabled 设置为false,则直接关闭了二级缓存,Mapper XML文件中的配置无效。
  2. cacheEnabled 设置为true(默认值),只有Mapper XML文件中配置了 cache 时该文件对应的 namespace 才会启用二级缓存

实现原理

  1. cacheEnabled 为true时,使用 <font style="color:#333333;background-color:#FDFDFD;">CachingExecutor</font> 装饰 <font style="color:#333333;background-color:#FDFDFD;">Executor</font>进入一级缓存的查询流程前,先在 <font style="color:#333333;background-color:#FDFDFD;">CachingExecutor</font> 进行二级缓存的查询;<font style="color:#333333;background-color:#FDFDFD;">cacheEnabled</font> 为false时,直接使用 <font style="color:#333333;background-color:#FDFDFD;">Executor</font> ,此时全局关闭二级缓存。
  2. 当设置了 cache 时,Mapper XML文件中的所有 MappedStatement 会关联一个 CacheCacheExecutor 会判断 MappedStatement 是否关联了 Cache ,若无,则不进行二级缓存的命中判断,直接调用 Executor 进行操作。
  1. SqlSession: 对外提供了用户和数据库之间交互需要的所有方法,隐藏了底层的细节。默认实现类是DefaultSqlSession
  2. ExecutorSqlSession向用户提供操作数据库的方法,但和数据库操作有关的职责都会委托给Executor
  3. BaseExecutorBaseExecutor是一个实现了Executor接口的抽象类,定义若干抽象方法,在执行的时候,把具体的操作委托给子类进行执行。
  4. Cache: MyBatis中的Cache接口,提供了和缓存相关的最基本的操作,

其他配置

除了上述配置外,还有每个SQL语句上的两个配置可以同时对一级缓存和二级缓存生效。

设置 参数描述 有效值 默认值
flushCache 将其设置为 true,任何时候只要语句被调用,都会导致本地缓存和二级缓存都会被清空 true false select默认值为false,update,insert,delete默认为true
useCache 将其设置为 true,将会导致本条语句的结果被二级缓存 true false 对 select 元素为 true,update,insert,delete无此配置

参考

  1. 聊聊MyBatis缓存机制
  2. XML 映射配置文件
  3. Mapper XML 文件