持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第2天,点击查看活动详情
一级缓存
1、一级缓存的组成
MyBatis 开启一次和数据库的会话,MyBatis 会创建出一个SqlSession对象表示一次数据库会话。
SqlSession:只是一个MyBatis对外的接口,SqlSession将它的工作交给了Executor执行器这个角色来完成,负责完成对数据库的各种操作
Executor:执行器接口类,负责完成对数据库的各种操作,以及维护缓存信息
Cache:缓存接口类,处理缓存信息
PerpetualCache:Cache接口实现类,通过一个简单的HashMap实现缓存
三者之间关系
SqlSession的实现类定义一个成员变量Executor,Executor的实现类BaseExecutor拥有Cache接口的一个实现类PerpetualCache;
2、什么是一级缓存
前提
在对数据库的一次会话中,我们有可能会反复地执行完全相同的查询语句,而我们在极短的时间内做了完全相同的查询,那么它们的结果极有可能完全相同,由于查询一次数据库的代价很大,这有可能造成很大的资源浪费。
目的
减少资源的浪费
实现
1、MyBatis会在表示会话的SqlSession对象中建立一个简单的缓存,将每次查询到的结果结果缓存起来
2、当下次查询的时候,如果判断先前有个完全一样的查询,会直接从缓存中直接将结果取出,返回给用户
3、否则,从数据库读取数据,将查询结果存入缓存并返回给用户。
3、一级缓存的生命周期
a、MyBatis在开启一个数据库会话时,会 创建一个新的SqlSession对象,SqlSession对象中会有一个新的Executor对象,Executor对象中持有一个新的PerpetualCache对象;当会话结束时,SqlSession对象及其内部的Executor对象还有PerpetualCache对象也一并释放掉。
b、如果SqlSession调用了close() 方法,会释放掉一级缓存PerpetualCache对象,一级缓存将不可用;
c、如果SqlSession调用了clearCache() ,会清空PerpetualCache对象中的数据,但是该对象仍可使用;
d、SqlSession中执行了任何一个update操作 (update()、delete()、insert()) ,都会清空PerpetualCache对象的数据,但是该对象可以继续使用;
注
1、spirng项目开启事务时,同一个事务使用同一个SqlSession,
2、未开启事务时,每次请求都会关闭之前的SqlSession,再创建新的SqlSession,所以导致一级缓存完全没有使用到。
4、一级缓存工作流程
-
对于某个查询,根据statementId,params,rowBounds来构建一个key值,根据这个key值去缓存Cache中取出对应的key值存储的缓存结果;
-
判断从Cache中根据特定的key值取的数据数据是否为空,即是否命中;
-
如果命中,则直接将缓存结果返回;
-
如果没命中:
4.1 去数据库中查询数据,得到查询结果;
4.2 将key和查询到的结果分别作为key,value对存储到Cache中;
4.3 将查询结果返回;
-
结束。
5、CacheKey的定义
组成:statementId + rowBounds + 传递给JDBC的SQL + 传递给JDBC的参数值
statementId:代表着将执行什么样的Sql
rowBounds:查询时要求的结果集中的结果范围;MyBatis自身提供的分页功能是通过RowBounds来实现的,它通过rowBounds.offset和rowBounds.limit来过滤查询出来的结果集
传递给JDBC的SQL:调用JDBC的时候,传入的SQL语句要完全相同
传递给JDBC的参数值:传递给JDBC的参数值也要完全相同
目的
1、根据CacheKey作为key,去Cache缓存中查找缓存结果;
2、如果查找缓存命中失败,则通过此CacheKey作为key,将从数据库查询到的结果作为value,组成key,value对存储到Cache缓存中。
6、性能问题
1、MyBatis对会话(Session)级别的一级缓存设计的比较简单,就简单地使用了HashMap来维护,并没有对HashMap的容量和大小进行限制。
如果一直使用某一个SqlSession对象查询数据,这样会不会导致HashMap太大,而导致OOM错误?
MyBatis这样设计也有它自己的理由:
a. 一般而言SqlSession的生存时间很短。一般情况下使用一个SqlSession对象执行的操作不会太多,执行完就会消亡;
b. 对于某一个SqlSession对象而言,只要执行update操作(update、insert、delete),都会将这个SqlSession对象中对应的一级缓存清空掉,所以一般情况下不会出现缓存过大,影响JVM内存空间的问题;
c. 可以手动地释放掉SqlSession对象中的缓存。
2、一级缓存是一个粗粒度的缓存,没有更新缓存和缓存过期的概念
MyBatis的一级缓存就是使用了简单的HashMap,MyBatis只负责将查询数据库的结果存储到缓存中去, 不会去判断缓存存放的时间是否过长、是否过期,因此也就没有对缓存的结果进行更新这一说了。
根据特性,需要注意: 1、对于数据变化频率很大,并且需要高时效准确性的数据要求,我们使用SqlSession查询的时候,要控制好SqlSession的生存时间,SqlSession的生存时间越长,它其中缓存的数据有可能就越旧,从而造成和真实数据库的误差;同时对于这种情况,用户也可以手动地适时清空SqlSession中的缓存;
2、对于只执行、并且频繁执行大范围的select操作的SqlSession对象,SqlSession对象的生存时间不应过长。