现代化的中国风网站,一键下载微信,免费图文模板,菜谱网站模版MyBatis的缓存实现原理主要基于三级缓存机制#xff0c;包括一级缓存#xff08;本地缓存#xff09;、二级缓存#xff08;全局缓存#xff09;和三级缓存#xff08;跨会话缓存#xff09;。这个缓存在我们实际开发中可以避免我们查询重复的数据#xff0c;在一定程度…MyBatis的缓存实现原理主要基于三级缓存机制包括一级缓存本地缓存、二级缓存全局缓存和三级缓存跨会话缓存。这个缓存在我们实际开发中可以避免我们查询重复的数据在一定程度上可以帮助我们减少对数据库同一数据的重复查询也可以在一定程度上使用MyBatis缓存可以帮助我们更好的查询数据和进行数据交互减少对数据库的数据查询次数吧。
一级缓存
MyBatis一级缓存也可以称作本地缓存他是SqlSession级别的缓存默认开启可以减少我们对数据库的重复查询当执行查询的时候查询结果会被存储在SqlSession中的本地存储中在同一个Session中如果执行相同的查询MyBatis会从本地缓存中查找结果如果找到则直接返回否则再去数据库中查询并存储到本地缓存中。
一级缓存的实现
// 在MyBatis配置文件中开启一级缓存
configurationsettingssetting namelocalCacheScope valueSESSION//settings
/configurationpublic static void main(String[] args) {try (SqlSession sqlSession sqlSessionFactory.openSession()) {EmpMapper empMapper sqlSession.getMapper(EmpMapper.class);// 测试一级缓存Emp emp1 empMapper.getEmpById(1); // 第一次查询会从数据库中获取数据Emp emp2 empMapper.getEmpById(1); // 第二次查询会从一级缓存中获取数据System.out.println(emp1 emp2); // 输出true说明从一级缓存中获取到了同一个对象}}一级缓存失效的几种情况 不同的SqlSession一级缓存是基于SqlSession的当使用不同的SqlSession对象执行相同的查询时一级缓存会失效。因为每个SqlSession都有自己的本地缓存无法共享缓存数据。 手动清空缓存通过调用SqlSession的clearCache()方法可以手动清空一级缓存。这样会导致之前缓存的数据被清除下一次查询会重新从数据库中获取数据。 更新操作当执行了插入、更新或删除操作时可能会影响到缓存中的数据。MyBatis会自动将更新操作同步到缓存中但是会清空相关的缓存数据以保证缓存的数据与数据库的数据一致。 缓存大小限制一级缓存的大小是有限的默认情况下一级缓存的大小为1024个对象。当缓存中的对象数量达到上限时新的查询结果会导致最早的查询结果被淘汰出缓存从而失效。 手动提交事务如果在执行查询之前手动提交了事务调用了commit()方法则会导致一级缓存失效。因为事务提交后会关闭当前的SqlSession同时清空一级缓存。
二级缓存
MyBatis二级缓存也可以被称作全局缓存一般上是默认关闭的并且配置方式也与一级缓存有区别需要我们在具体的mapper映射文件中手动配置开启。
二级缓存开启的条件 因为二级缓存是SqlSessionFactory级别通过同一个SqlSessionFactory创建的SqlSession查询的结果被缓存伺候再次执行相同的查询语句就可以直接从缓存中获取。 在核心配置文件中设置全局配置属性cacheEnabletrue,默认true,一般不需要设置在映射文件中配置cache /二级缓存必须在SqlSession关闭或者提交后有效查询的数据所转换的实体类类型必须实现序列化的接口。 当我们开启二级缓存的时候查询的结果会被存储在Mapper的全局缓存中多个SqlSession可以共享同一个Mapper的二级缓存在执行查询的时候MyBatis会从我们的二级缓存中查询结果如果找到则直接返回如果没有找到那么就会去数据库中查询并存储到二级缓存中。
// 在Mapper接口对应的映射文件中开启二级缓存
cache/public static void main(String[] args) {try (SqlSession sqlSession sqlSessionFactory.openSession()) {EmpMapper empMapper sqlSession.getMapper(EmpMapper.class);// 测试二级缓存SqlSession sqlSession2 sqlSessionFactory.openSession();EmpMapper empMapper2 sqlSession2.getMapper(EmpMapper.class);Emp emp3 empMapper2.getEmpById(1); // 第一次查询会从数据库中获取数据sqlSession2.close(); // 关闭SqlSessionSqlSession sqlSession3 sqlSessionFactory.openSession();EmpMapper empMapper3 sqlSession3.getMapper(EmpMapper.class);Emp emp4 empMapper3.getEmpById(1); // 第二次查询会从二级缓存中获取数据sqlSession3.close();System.out.println(emp3 emp4); // 输出true说明从二级缓存中获取到了同一个对象}}cache标签的配置属性值 eviction指定缓存的回收策略用于决定在缓存达到上限时如何清理缓存。常用的回收策略包括 LRULeast Recently Used最近最少使用根据最近的访问时间来淘汰数据。 FIFOFirst In, First Out先进先出根据数据最早进入缓存的时间来淘汰数据。SOFT软引用根据JVM的垃圾回收机制来决定是否清理缓存数据。WEAK弱引用类似于软引用但更容易被垃圾回收器回收。 flushInterval指定缓存刷新间隔表示缓存刷新的时间间隔单位为毫秒。设置了该属性后缓存会定期刷新以保证缓存数据的有效性。 readOnly指定缓存是否为只读如果设置为true表示缓存数据不会被修改可以提高缓存的性能。 size指定缓存的大小限制表示缓存中可以存储的对象数量上限。当缓存中的对象数量达到该上限时会根据回收策略进行数据清理。 type指定缓存的实现类型可以是MyBatis提供的内置缓存实现也可以是自定义的缓存实现。常用的内置缓存实现包括 PERPETUAL永久缓存数据永久保存在缓存中。FIFO先进先出缓存。 LRU最近最少使用缓存。SOFT软引用缓存。WEAK弱引用缓存。
示例配置
cacheevictionLRUflushInterval60000readOnlyfalsesize1024typePERPETUAL/MyBatis缓存查询顺序
先查询二级缓存因为二级缓存中可能会有其他程序已经查出来的数据可以拿出来使用如果二级缓存没有命中那就在查一级缓存如果一级缓存也没有命中那就查询数据库。SqlSession关闭后以及缓存中的数据会写入到二级缓存中。
二级缓存失效的几种原因
数据更新当执行了插入、更新或删除操作时会导致与这些操作相关的缓存数据失效。MyBatis会自动将更新操作同步到缓存中但也会清空相关的缓存数据以保证缓存中的数据与数据库的数据一致。
并发操作在并发环境下如果多个线程同时对同一条数据进行更新操作可能会导致缓存中的数据与数据库中的数据不一致。这时需要考虑缓存的并发控制策略以避免脏数据的产生。
查询结果不满足缓存条件MyBatis的二级缓存对查询结果有一定的条件限制例如查询结果的类型必须是可序列化的、不能包含动态SQL等。如果查询结果不满足这些条件可能会导致缓存失效。
缓存大小限制二级缓存的大小是有限的当缓存中的对象数量达到上限时新的查询结果会导致最早的查询结果被淘汰出缓存从而失效。
手动清空缓存通过调用SqlSession的clearCache()方法可以手动清空二级缓存。这样会导致之前缓存的数据被清除下一次查询会重新从数据库中获取数据。
事务提交如果在执行查询之前手动提交了事务调用了commit()方法则会导致二级缓存失效。因为事务提交后会关闭当前的SqlSession同时清空二级缓存。
三级缓存
三级缓存是一种跨会话级别的缓存他通过与外部缓存系统例如RedisMemcached等进行实现。通过外部缓存系统的集成进而实现多个应用实例之间的缓存共享从而提高缓存的利用率和拓展性。
关于三级缓存无法依靠MyBatis单独实现因为他本事不提供直接对外缓存系统的集成故而无法实现一个完整的三级缓存示例然而可以通过MyBatis的扩展插件或者自定义实现来集成外部缓存系统例如:RedisMemcached等。 我们采用Redis自定义实现Mybatis的三级缓存。 首先需要引入MyBatis的扩展插件例如MyBatis Redis Cache它是一个MyBatis的缓存插件可以将缓存数据存储到Redis中。
然后需要在MyBatis的配置文件中配置该插件指定Redis作为缓存的实现
configurationsettingssetting namecacheEnabled valuetrue//settingstypeAliases!-- 定义需要缓存的实体类别名 --/typeAliasesenvironments defaultdevelopmentenvironment iddevelopmenttransactionManager typeJDBC/dataSource typePOOLED!-- 配置数据源信息 --/dataSource/environment/environmentsmappers!-- 配置Mapper接口 --/mapperspluginsplugin interceptororg.mybatis.caches.redis.RedisCache//plugins
/configurationplugin标签配置了MyBatis Redis Cache插件将Redis作为缓存的实现。
接下来可以在Java代码中进行测试通过调用Mapper接口的方法来触发缓存的使用
public class MyBatisCacheTest {public static void main(String[] args) {try (SqlSession sqlSession sqlSessionFactory.openSession()) {EmpMapper empMapper sqlSession.getMapper(EmpMapper.class);// 调用Mapper接口的方法进行查询Emp emp1 empMapper.getEmpById(1); // 第一次查询会从数据库中获取数据Emp emp2 empMapper.getEmpById(1); // 第二次查询会从Redis缓存中获取数据System.out.println(emp1 emp2); // 输出true说明从Redis缓存中获取到了同一个对象}}
}实际的三级缓存实现可能会更加复杂涉及到缓存的清理、失效策略、缓存击穿和雪崩等问题需要根据实际情况进行更加细致的配置和测试。