Mybatis一级缓存二级缓存使用总结
Mybatis缓存简单使用
MyBatis中的缓存分为一级缓存和二级缓存,一级缓存又被称为 SqlSession 级别的缓存,二级缓存又被称为表级缓存。
一级缓存
一级缓存是 SqlSession级别 的缓存。在操作数据库时需要构造 sqlSession 对象,在对象中有一个(内存区域)数据结构(HashMap)用于存储缓存数据。不同的 sqlSession 之间的缓存数据区域(HashMap)是互相不影响的。用一张图来表示一下一级缓存,其中每一个 SqlSession 的内部都会有一个一级缓存对象。
在应用运行过程中,我们有可能在一次数据库会话中,执行多次查询条件完全相同的SQL,MyBatis 提供了一级缓存的方案优化这部分场景,如果是相同的SQL语句,会优先命中一级缓存,避免直接对数据库进行查询,提高性能
一级缓存使用条件
- 必须是相同的SQL和参数
- 必须是相同的会话
- 必须是相同的namespace 即同一个mapper
- 必须是相同的statement 即同一个mapper 接口中的同一个方法
- 查询语句中间没有执行session.clearCache() 方法
- 查询语句中间没有执行 insert update delete 方法(无论变动记录是否与 缓存数据有无关系)
一级缓存失效条件:
- 查询中途有增删改操作
- 不同SqlSession会话
- 同一SqlSession 使用不同的查询条件
- 使用
sqlSession.clearCache()
手动清理缓存
二级缓存
业务系统中存在很多的静态数据如,字典表、菜单表、权限表等,这些数据的特性是不会轻易修改但又是查询的热点数据。一级缓存针对的是同一个会话当中相同SQL,并不适合这情热点数据的缓存场景。为了解决这个问题引入了二级缓存,它脱离于会话之外。
查询流程:二级缓存->一级缓存->数据库
注意!!! 会话关闭之后才会触发缓存!
图中namespace1 namespace2所在灰色区域,就是二级缓存!
开启二级缓存方式:
在mapper接口上,加@CacheNamespace注解
//使用示例:
@CacheNamespace()
public interface LabelMapper {
@Select("select * from t_label where id =#{id}")
Label getById(Integer id);
}
@CacheNamespace注解属性说明
@CacheNamespace(
implementation = PerpetualCache.class, // 缓存实现 Cache接口 实现类
eviction = LruCache.class,// 缓存算法
flushInterval = 60000, // 刷新间隔时间 毫秒
size = 1024, // 最大缓存引用对象
readWrite = true, // 是否可写
blocking = false // 是否阻塞
)
二级缓存使用条件:
- 当会话提交或关闭之后,才会填充二级缓存
- 必须是在同一个命名空间之下
- 必须是相同的statement 即同一个mapper接口中的同一个方法
- 必须是相同SQL语句和参数
- 如果readWrite = true(@CacheNamespace 注解的参数,默认就是true),实体对象必须实现Serializable接口
二级缓存清除条件:
- 只有修改会话提交之后,才会执行清空操作
- 任何一种增删改操作,都会清空整个namespace中的缓存
- xml配置通过
标签开启的缓存,是另外一个namespace的缓存,所以不会命中
是否应该使用二级缓存?
那么究竟应该不应该使用二级缓存呢?先来看一下二级缓存的注意事项:
- 缓存是以
namespace
为单位的,不同namespace
下的操作互不影响。 - insert,update,delete操作会清空所在
namespace
下的全部缓存。 - 通常使用MyBatis Generator生成的代码中,都是各个表独立的,每个表都有自己的
namespace
。 - 多表操作一定不要使用二级缓存,因为多表操作进行更新操作,一定会产生脏数据。
如果你遵守二级缓存的注意事项,那么你就可以使用二级缓存。
但是,如果不能使用多表操作,二级缓存不就可以用一级缓存来替换掉吗?而且二级缓存是表级缓存,开销大,没有一级缓存直接使用 HashMap 来存储的效率更高,所以二级缓存并不推荐使用