MyBatis缓存机制
MyBatis缓存实现原理
一级缓存
一级缓存是如何组织的?
一级缓存是SqlSession级别的缓存。在操作数据库时需要构造 sqlSession对象,并为SqlSession对象创建一个Executor执行器,而缓存信息以HashMap的形式维护在这个Executor中,MyBatis将缓存和缓存操作封装在Cache中。
一级缓存原理
第一次发出一个查询sql,sql查询结果写入SqlSession的一级缓存中,缓存使用的数据结构为(HashMap)。
同一个SqlSession发出第二次查询时,就从缓存中获取。如果两次中间出现commit操作(update/insert/delete),则本SqlSession的一级缓存全部清空(为了缓存中记录最新的数据,避免脏读),从数据库查询。
- key:mapperID+offset+limit+Sql+所有的入参
- value:查询结果
一级缓存的生命周期
- 开启数据库会话,创建新的SqlSession对象,SqlSession对象中会有一个Executor对象,Executor对象持有新的PerpetualCache对象;会话结束时,SqlSession对象以及Executor对象和PerpetualCache对象一并销毁。
- SqlSession调用close()方法,会释放掉一级缓存PerpetualCache对象,一级缓存不可用。
- 如果SqlSession调用clearCache()方法,会清空PerpetualCache对象,但对象仍可用。
- SqlSession执行任何一个insert()、update()、delete()操作,会清空PerpetualCache对象,但对象仍可用。
一级缓存的工作流程
- 对于某个查询,根据statementId、params、rowBounds来构建key值,根据key值去缓存Cache中查找结果;
- 判断从Cache中根据特定的key值取的数据数据是否为空,即是否命中;
- 如果命中,则直接返回查询结果;
- 如果没有命中:
- 数据库中查询结果;
- 将key和查询结果分别作为key和value存入Cache缓存中;
- 查询结果返回。
- 结束。
一级缓存性能分析
- Session级别的一级缓存设计比较简单,只使用了HashMap来维护,并没有对HashMap的容量和大小进行限制。
- 一级缓存是粗粒度缓存,没有更新缓存和缓存过期概念。
二级缓存
二级缓存是mapper级别的,mapper以命名空间为单位创建缓存数据结构(HashMap),二级缓存通过CachingExecutor(Executor的代理对象)实现。
二级缓存原理
所有查询操作,在CachingExecutor都会先匹配缓存中是否存在,否则查询数据库。
- key:mapperID+offset+limit+Sql+所有的入参
- value:查询结果
二级缓存工作模式
二级缓存划分
MyBatis并不简单将Application就只有一个Cache缓存对象,它将缓存划分的更新,即是mapper级别的(即每一个mapper一个缓存对象)。
1. 为每一个mapper分配一个缓存对象(对于每一个mapper.xml,在其中使用<cache> 节点)。
2. 多个mapper共用一个缓存对象(使用<cache-ref namespace="">节点,来指定你的这个Mapper使用到了哪一个Mapper的Cache缓存)。
二级缓存的开启
MyBatis对二级缓存的支持粒度较细,需要具体指定到某条查询语句上。如果要开启二级缓存,需做到如下三点:
- MyBatis支持二级缓存的总开关:全局配置变量参数【cacheEnabled=true】。
- 该select语句所在的Mapper,配置了<cache> 或<cached-ref>节点,并且有效。
- 该select语句的参数 useCache=true。
二级缓存的实现
二级缓存的实现有三种选择:
-
MyBatis自身提供的缓存实现;
-
MyBatis定义了大量的Cache的装饰器来增强Cache缓存的功能
-
对于每个Cache而言,都有容量限制,MyBatis提供了各种策略来对Cache缓存的容量进行控制,以及对于Cache中的数据进行刷新和置换。主要有以下几个刷新和置换策略:
-
用户自定义的Cache接口实现;
跟第三方内存缓存库的集成;
二级缓存应用场景
-
使用场景
对于访问响应速度要求高,但是实时性不高的查询,可以采用二级缓存技术。 -
注意事项
在使用二级缓存的时候,要设置一下刷新间隔(cache标签中有一个flashInterval属性)来定时刷新二级缓存,这个刷新间隔根据具体需求来设置,比如设置30分钟、60分钟等,单位为毫秒。
二级缓存局限性
Mybatis二级缓存对细粒度的数据级别的缓存实现不好。
场景
对商品信息进行缓存,由于商品信息查询访问量大,但是要求用户每次查询都是最新的商品信息,此时如果使用二级缓存,就无法实现当一个商品发生变化只刷新该商品的缓存信息而不刷新其他商品缓存信息,因为二级缓存是mapper级别的,当一个商品的信息发送更新,所有的商品信息缓存数据都会清空。解决方法
此类问题,需要在业务层根据需要对数据有针对性的缓存。
比如可以对经常变化的 数据操作单独放到另一个namespace的mapper中。