Mybatis一级缓存与二级缓存
缓存就是内存中的数据,常常来自对数据库查询结果的保存,使用缓存可以避免频繁与数据库进行交互,从而提高查询响应速度。
mybaits提供内存支持;
一级缓存
一级缓存,sqlSession级别的缓存,mybatis级的缓存是默认开启的;
使用场景:
用户发起查询请求,查找某条数据,sqlSession 先去缓存中查找,是否有该数据,如果有,读取;
如果没有,从数据库中查询,并将查询到的数据放入一级缓存区域,供下次查找使用。
但 sqlSession 执行commit,即增删改操作时会清空缓存。这么做的目的是避免脏读。
如果commit不清空缓存,会有以下场景:
A查询了某商品库存为10件,并将10件库存的数据存入缓存中,之后被客户买走了10件,数据被delete了,但是下次查询这件商品时,并不从数据库中查询,而是从缓存中查询,就会出现错误。
一级缓存失效场景
不同的SqlSession对应不同的一级缓存
必须使用同一SqlSession,执行同一个查询方法;
如果创建不同的SqlSession,即便执行同一个查询方法,也不会缓存。
同一个SqlSession,但是查询条件不同;
同一个SqlSession两次查询期间执行了任何一次的增删改操作;
同一个SqlSession两次查询期间手动清空了缓存。 sqlSession.clearCache();
有了一级缓存,那么为什么要提供二级缓存呢?
二级缓存是 mapper 级别的缓存,多个SqlSession去操作同一个Mapper的sql语句,多个SqlSession可以共用二级缓存,二级缓存是跨SqlSession的。二级缓存的作用范围更大。
还有一个原因,实际开发中,MyBatis 通常和 Spring 进行整合开发。
Spring 将事务放到 Service 中管理,对于每一个 service 中的 sqlsession 是不同的,
这是通过 mybatis-spring 中的 org.mybatis.spring.mapper.MapperScannerConfigurer 创建 sqlsession 自动注入到 service 中的。
每次查询之后都要进行关闭 sqlSession ,关闭之后数据被清空。
所以 spring 整合之后,如果没有事务,一级缓存 是没有意义的。
二级缓存
二级缓存是 SqlSessionFactory 级别,通过 SqlSessionFactory 创建SqlSession 查询的结果会被缓存。 此后若再次执行相同的查询,结果就会从缓存中获取。mapper级别的缓存,同一个namespace公用这一个缓存,所以对SqlSession是共享的;
二级缓存:
开启二级缓存后,用户查询时,会先去二级缓存中找,找不到了再去一级缓存中找;
一级缓存也没有查询到,则查询数据库;
二级缓存的流程图
开启二级缓存:
<settings> <!-- 开启所有Mapper的二级缓存 --> <setting name="cacheEnabled" value="true"/>
</settings>
简化配置:
<cache/>
POJO 类实现 Serializable 接口
public class User implements Serializable {
}
二级缓存必须在SqlSession关闭或提交之后有效
sqlSession1.close(); // 或者 sqlSession1.commit();
sqlSession2.close(); // 或者 sqlSession2.commit();
一二级缓存的弊端
- 没有缓存命中率
- MyBatis的一级缓存只适用于同一个SqlSession,因此它在多个SqlSession之间是无效的。这意味着如果您需要执行多个查询,并且这些查询需要在不同的SqlSession中执行,那么一级缓存就无法提供任何帮助。这种情况下,每次查询都需要从数据库中获取数据,因此缓存命中率为0。
- 线程安全问题
- MyBatis的SqlSession并不是线程安全的,因此在多线程环境下使用一级缓存可能会导致问题。如果两个线程共享同一个SqlSession,并且其中一个线程执行了一个查询,那么另一个线程可能会从缓存中获取到不正确的结果。因此,如果您在多线程环境中使用MyBatis,请确保每个线程都有自己的SqlSession。
- 没有清除机制
- MyBatis的一级缓存没有自动清除机制。这意味着如果您在SqlSession中执行了更新、插入或删除操作,那么这些操作将会使缓存失效。但是,如果您在执行这些操作之前没有清除缓存,那么缓存中的数据将会是旧的,这可能会导致应用程序中的错误。
- 内存泄漏问题
- MyBatis的一级缓存存储在内存中,因此如果您在应用程序中长时间使用同一个SqlSession,那么缓存中的数据可能会占用大量内存。这可能会导致内存泄漏问题,尤其是在长时间运行的应用程序中。
基于这些问题,我们可以看出MyBatis的一级缓存并不是最好的选择。因此,如果您需要缓存查询结果以提高性能,可以考虑使用二级缓存。
- MyBatis的一级缓存存储在内存中,因此如果您在应用程序中长时间使用同一个SqlSession,那么缓存中的数据可能会占用大量内存。这可能会导致内存泄漏问题,尤其是在长时间运行的应用程序中。