背景
mybatis提供了缓存功能,但是如果配置不当就会出现脏数据的问题,因此需要了解mybatis缓存的原理以及如何对缓存进行设置。
基本概念
mybatis中提供了一级缓存和二级缓存,一级缓存是 SqlSession
级别的,也即同一个 SqlSession
实例执行的SQL语句会使用同一份缓存。(至于 SqlSession
是一个线程一个实例,还是一个Http请求一个实例,则要看项目的设置,Spring中 <font style="color:#F5222D;">SqlSession</font>
级别待补充)
一级缓存缺点:
- 只要
SqlSession
执行了一次update
操作,就会clear一级缓存,不论update
和select
是否是同一个namespace
。因此一级缓存只有在连续select
的情况下才会命中,命中的概率不高。 - 若
<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
下的 update
和 select
才会互相影响,同时解决了两个 SqlSession
操作同一份数据导致的脏数据问题。
一级缓存
配置项
可以通过mybatis配置文件中的 settings 对一级缓存进行配置。
设置 | 参数描述 | 有效值 | 默认值 | |
---|---|---|---|---|
localCacheScope | MyBatis 利用本地缓存机制(Local Cache)防止循环引用(circular references)和加速重复嵌套查询。 默认值为 SESSION,这种情况下会缓存一个会话中执行的所有查询。 若设置值为 STATEMENT,本地会话仅用在语句执行上,对相同 SqlSession 的不同调用将不会共享数据。 | SESSION | STATEMENT | SESSION |
当 localCacheScope
为 STATEMENT
时,每执行一个SQL语句就会将一级缓存清空,相当于关闭一级缓存。
实现原理
一级缓存实现类是 BaseExecutor
中的关联的一个 PerpetualCache
, PerpetualCache
内部使用 HashMap
存放缓存,由于一个 SqlSession
关联一个 Executor
,所以一个 SqlSession
关联一个 PerpetualCache
,也即一级缓存是 SqlSession
级别的。
二级缓存
配置
可以通过mybatis配置文件中的 settings 对二级缓存进行配置。
设置 | 参数描述 | 有效值 | 默认值 | |
---|---|---|---|---|
cacheEnabled | 全局地开启或关闭配置文件中的所有映射器已经配置的任何缓存。 | true | false | true |
也可以通过Mapper XML文件中的 cache
和 cache-ref
进行配置
- cache – 给定命名空间的缓存配置。
- cache-ref – 其他命名空间缓存配置的引用。
- 若
cacheEnabled
设置为false,则直接关闭了二级缓存,Mapper XML文件中的配置无效。- 若
cacheEnabled
设置为true(默认值),只有Mapper XML文件中配置了cache
时该文件对应的namespace
才会启用二级缓存
实现原理
- 当
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>
,此时全局关闭二级缓存。 - 当设置了
cache
时,Mapper XML文件中的所有MappedStatement
会关联一个Cache
,CacheExecutor
会判断MappedStatement
是否关联了Cache
,若无,则不进行二级缓存的命中判断,直接调用Executor
进行操作。
- SqlSession: 对外提供了用户和数据库之间交互需要的所有方法,隐藏了底层的细节。默认实现类是
DefaultSqlSession
。- Executor:
SqlSession
向用户提供操作数据库的方法,但和数据库操作有关的职责都会委托给Executor- BaseExecutor:
BaseExecutor
是一个实现了Executor接口的抽象类,定义若干抽象方法,在执行的时候,把具体的操作委托给子类进行执行。- Cache: MyBatis中的Cache接口,提供了和缓存相关的最基本的操作,
其他配置
除了上述配置外,还有每个SQL语句上的两个配置可以同时对一级缓存和二级缓存生效。
设置 | 参数描述 | 有效值 | 默认值 | |
---|---|---|---|---|
flushCache | 将其设置为 true,任何时候只要语句被调用,都会导致本地缓存和二级缓存都会被清空 | true | false | select默认值为false,update,insert,delete默认为true |
useCache | 将其设置为 true,将会导致本条语句的结果被二级缓存 | true | false | 对 select 元素为 true,update,insert,delete无此配置 |