一级缓存
MyBatis的一缓存其实就是一个SQLSession级别的,意思就是sqlsession只能访问自己的一级缓存的数据。一级缓存查询存在于每一个的sqlsession类的实例对象中,当第一次查询某一个数据时候,sqlsession类的实例对象会将该数据存入到一级缓存。这样可以在没有收到改变该数据的请求之前,我们所查询数据都是从缓存中去获取的,而不是从数据库中去取数据,这样就大大减少数据库的频繁查询,导致效率降低的原因。
MyBatis的一级缓存是默认开启
一级缓存原理
首先用户第一次查询sql时候,sql的查询结果就会被写入sqlsession一级缓存中的,这样用户第二次查询时,直接从一级缓存取出数据,而不是数据库。
如果用户出现commit操作时,比如增删改查,这时sqlsession中一级缓存区域就会全部清空。清空之后再次去一级缓存查找不到,就会走数据库进行查找,然后再次存到缓存中。注意:缓存使用的数据结构也是map的。
二级缓存
二级缓存的范围就是mapper级别,也就是mapper以命名空间为单位创建缓存数据结构,也是map结构。二级缓存和 一级缓存一样的是,二级缓存的多个sqlsession去操作同一个mapper映射的sql语句,然后多个sqlsession可以共用二级缓存这样的一个思想,它是跨sqlsession的。
MyBatis的二级缓存是默认关闭
二级缓存实战
# 二级缓存全局开关. 默认 true
mybatis:
configuration:
cache-enabled: true
源码解读
package org.apache.ibatis.session;
public class Configuration {
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
// ...
// 当cacheEnabled为true的时候,底层使用的Executor才是支持二级缓存的CachingExecutor
if (cacheEnabled) {
executor = new CachingExecutor(executor);
}
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
}
}
<mapper namespace="com.jaemon.mapper.OrganMapper">
<!-- 开启 mapper 下的二级缓存 -->
<cache eviction="LRU" flushInterval="30000" />
<!--
flushCache="true": 该语句的执行结果,会清空本地缓存以及二级缓存
useCache="true": 该语句的执行结果,会被缓存到到二级缓存
-->
<select id="queryOrgById" resultType="com.jaemon.domain.dto.OrgDTO" useCache="true">
select id, org_name, org_type
from sys_org
where id = #{id}
</select>
</mapper>
返回的POJO对象需要实现java.io.Serializable的接口。 public class OrgDTO implements Serializable { }
@Service
public class OrganServiceImpl implements IOrganService {
@Autowired
private OrganMapper organMapper;
@Override
public List<OrgDTO> queryOrgById(Long id) {
List<OrgDTO> orgs1 = organMapper.queryOrgById(id);
List<OrgDTO> orgs2 = organMapper.queryOrgById(id);
return orgs1;
}
}
如果需要验证一级缓存, 可以用一个事务包裹起来, 让他们处于同一个sqlSession中
@Override
@Transactional(rollbackFor = Exception.class)
public List<OrgDTO> queryOrgById(Long id) {
List<OrgDTO> orgs1 = organMapper.queryOrgById(id);
List<OrgDTO> orgs2 = organMapper.queryOrgById(id);
return orgs1;
}
cache 属性
- eviction: 代表的是缓存回收策略,目前MyBatis提供以下策略
- LRU: 最近最少使用的,一处最长时间不用的对象。 默认策略
- FIFO: 先进先出,按对象进入缓存的顺序来移除他们
- SOFT: 软引用,移除基于垃圾回收器状态和软引用规则的对象
- WEAK: 弱引用,更积极的移除基于垃圾收集器状态和弱引用规则的对象。这里采用的是LRU,移除最长时间不用的对形象
- flushInterval: 缓存刷新间隔,单位为毫秒。 缓存多长时间清空一次,默认不清空。
- size: 引用数目,一个正整数,代表缓存最多可以存储多少个对象,不宜设置过大。设置过大会导致内存溢出。这里配置的是1024个对象
- readOnly: 只读,意味着缓存数据只能读取而不能修改,这样设置的好处是我们可以快速读取缓存,缺点是我们没有
办法修改缓存。 默认 false - type: 可以指定自定义缓存,但是该类必须实现 org.apache.ibatis.cache.Cache 接口
cache-ref 标签
cache只对特定的Namespace使用,即每个namespace使用一个cache实例,如果要多个namespace使用同一个cache实例,则可以使用cache-ref来引用
有的时候可能我们多个不同的Mapper需要共享同一个缓存的,是希望在MapperA中缓存的内容在MapperB中可以直接命中的,这个时候我们就可以考虑使用cache-ref
<!-- MapperA.xml -->
<mapper namespace="com.jaemon.mapper.MapperA">
<cache />
</mapper>
<!-- MapperB.xml -->
<mapper namespace="com.jaemon.mapper.MapperB">
<cache-ref namespace="com.jaemon.mapper.MapperA"/>
</mapper>
<!-- MapperC.xml -->
<mapper namespace="com.jaemon.mapper.MapperC">
<cache />
</mapper>
二级缓存的应用场景
- 对于访问多的,实时性要求不高的查询请求,可以采用mybatis二级缓存技术。
- 对于查询特别耗时的sql请求,且实时性要求不高(如查询前24小时或前一个月的消费统计)
二级缓存如何应用
通过设置缓存刷新间隔时间flushInterval,由mybatis每隔一段时间自动清空缓存,可以根据数据变化频率设置刷新间隔,比如设置为30分钟、60分钟、24小时等,根据需求而定。
参考
- 理解Mybatis一级缓存,以及如何真正使用到一级缓存