1.简介
-
查询需要连表,会耗资源
-
优化:一次查询的结果存到内存中,即为缓存
-
再次查询可去缓存中查找
-
什么是缓存?(Cache)
-
存在内存中的临时数据。
-
将用户经常查询的数据放在缓存(内存)中,用户去查询数据就不用从磁盘上(关系型数据库数据文件)查询,从缓存中查询,从而提高查询效率,解决了高并发系统的性能问题。
-
-
为什么使用缓存?
-
减少和数据库的交互次数,减少系统开销,提高系统效率。
-
-
什么样的数据能使用缓存?
-
经常查询且不经常改变的数据
-
2.MyBatis缓存
-
MyBatis包含一个非常强大的查询缓存特性,它可以非常方便地定制和配置缓存。缓存可以极大的提升查询效率。
-
MyBatis系统中默认定义了两级缓存:一级缓存和二级缓存
-
默认情况下,只有一级缓存开启。(SqlSession级别的缓存,也称为本地缓存)
-
二级缓存需要手动开启和配置,他是基于namespace级别的缓存
-
为了提高扩展性,MyBatis定义了缓存接口Cache。我们可以通过实现Cache接口来自定义二级缓存
-
-
要启用全局的二级缓存,只需要在你的 SQL 映射文件中添加cache标签:
-
映射语句文件中的所有 select 语句的结果将会被缓存。
-
映射语句文件中的所有 insert、update 和 delete 语句会刷新缓存。
-
缓存会使用最近最少使用算法(LRU, Least Recently Used)算法来清除不需要的缓存。
-
缓存不会定时进行刷新(也就是说,没有刷新间隔)。
-
缓存会保存列表或对象(无论查询方法返回哪种)的 1024 个引用。
-
缓存会被视为读/写缓存,这意味着获取到的对象并不是共享的,可以安全地被调用者修改,而不干扰其他调用者或线程所做的潜在修改。
-
3.一级缓存
SqlSession创建到关闭这段时间内有效
-
测试步骤:
-
开启日志
-
测试查询用户,再一次session中查询两次相同的记录
@Test public void test(){ SqlSession sqlSession = MybatisUtils.getSqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); User user = mapper.getUserById(1); System.out.println(user); System.out.println("=============================="); User user1 = mapper.getUserById(1); System.out.println(user1); sqlSession.close(); }
-
查询同一条数据两遍,sql只走了一遍,在日志中可以查看
-
-
缓存失效的情况
-
增删改操作之后会刷新缓存,因为会修改数据
-
查询不同的数据,缓存会在第一次查询时存入
-
查询不同的Mapper
-
手动清理缓存:
sqlSession.clearCache();
-
-
一级缓存默认启用,就是一个map,用到时就取出
4.二级缓存
-
二级缓存也叫全局缓存,一级缓存作用域太低了,所以诞生了二级缓存
-
基于namespace级别的缓存,一个名称空间,对应一个二级缓存;
-
工作机制
-
一个会话查询一条数据,这个数据就会被放在当前会话的一级缓存中;
-
如果当前会话关闭了,这个会话对应的一级缓存就没了;但是我们想要的是,会话关闭了,一级缓存中的数据被保存到二级缓存中;
-
新的会话查询信息,就可以从二级缓存中获取内容;
-
不同的mapper查出的数据会放在自己对应的缓存(map)中;
-
-
步骤:
-
开启缓存,在mybatis的核心配置文件中开启
<!--默认是true,但还是需要显示的设置--> <setting name="cacheEnable" value="true"/>
-
在要使用二级缓存的Mapper.xml中添加cache标签
-
给cache标签添加属性
创建一个 FIFO 缓存,每隔 60 秒刷新,最多可以存储结果对象或列表的 512 个引用,而且返回的对象被认为是只读的,因此对它们进行修改可能会在不同线程中的调用者产生冲突。
<cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/>
-
测试
@Test public void test2(){ SqlSession sqlSession = MybatisUtils.getSqlSession(); SqlSession sqlSession2 = MybatisUtils.getSqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); User user = mapper.getUserById(1); System.out.println(user); System.out.println("=============================="); sqlSession.close(); UserMapper mapper2 = sqlSession2.getMapper(UserMapper.class); User user2 = mapper2.getUserById(1); System.out.println(user2); sqlSession2.close(); }
-
-
总结:
-
只要开启缓存二级缓存,在同一个Mapper下就有效
-
所有的数据都会先放在一级缓存中;
-
只有当会话提交,或者关闭的时候,才会提交到二级缓存中!
-
5.缓存原理
用户查询先看二级缓存中有没有
再看一级缓存中有没有
再查数据库
6.自定义缓存ehcache
Ehcache是一种广泛使用的开源Java分布式缓存。主要面向通用缓存
-
步骤
-
导包
<!--ehcache--> <dependency> <groupId>org.mybatis.caches</groupId> <artifactId>mybatis-ehcache</artifactId> <version>1.2.1</version> </dependency>
-
在Mapper.xml中配置cache
<!--使用第三方ehcache--> <cache type="org.mybatis.caches.ehcache.EhcacheCache"/>
-
编写缓存的properties文件
<?xml version="1.0" encoding="UTF-8"?> <ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd" updateCheck="false"> <!-- diskStore:为缓存路径,ehcache分为内存和磁盘两级,此属性定义磁盘的缓存位置。参数解释如下; user.home -用户主目录 user.dir -用户当前工作目录 java.io.tmpdir -默认临时文件路径 --> <diskStore path="./tmpdir/Tmp_EhCache"/> <defaultCache eternal="false" maxElementsInMemory="10000" overflowToDisk="false" diskPersistent="false" timeToIdleSeconds="1800" timeToLiveSeconds="259200" memoryStoreEvictionPolicy="LRU"/> <cache name="cloud_user" eternal="false" maxElementsInMemory="5000" overflowToDisk="false" diskPersistent="false" timeToIdleSeconds="1800" timeToLiveSeconds="1800" memoryStoreEvictionPolicy="LRU"/> <!--defaultCache:默认缓存策略,当ehcache找不到定义的缓存时,则使用这个缓存策略。只能定义一个。--> <!-- name:缓存名称。 maxELementsInMemory:级存最大数目 maxELementsOnDisk :硬盘最大级存个数。 eternaL:对象是否永久有效,一但设置了,timeout将不起作用。 overfLowToDisk:是否保存到磁盘,当系统宕机时 timeToldleSeconds:设置对象在失效前的允许闲置时间(单位:秒)。仅当eternal=false对象不是永久有效时使用,可选属性,默认值是0,也就是可闲置时间无穷大。 timeToLiveSeconds:设置对象在失效前允许存活时间(单位:秒)。最大时间介于创建时间和失效时间之间。仅当eternal=false对象不是永久有效时使用,默认值是0,也就是对象存活时间无穷大。 diskPersistent:是否缓存虚拟机重启期数据Whether the disk store persists between restarts of the Virtual Machine. The default value is false. diskSpoolBufferSizeMB:这个参数设置DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区。 diskExpiryThreadIntervalSeconds:磁盘失效线程运行时间间隔,默认是120秒。 memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。默认策略是LRU(最近最少使用)。你可以设置为FIFO(先进先出)或是LFU(较少使用)。 clearOnFlush:内存数量最大时是否清除。 memoryStoreEvictionPolicy:可选策略有:LRU(最近最少使用,默认策略)、FIFO(先进先出)、LFU (最少访问次数)。 FIFO:first in first out,这个是大家最熟的,先进先出。 LFU:Less Frequently Used,就是上面例子中使用的策略,直白一点就是讲一直以来最少被使用的。如上面所讲,缓存的元素有一个hit属性,hit值最小的将会被清出缓存。 LRU:Least Recently Used,最近最少使用的,缓存的元素有一个时间戳,当缓存容量满了,而又需要腾出地方来缓存新的元素的时候,那么现有缓存元素中时间戳离当前时间最远的元素将被清出缓存。 --> </ehcache>
-
-
自己编写缓存类
编写一个缓存工具类MyCache,实现Cache接口即可
-
以后使用最多的缓存是redis数据库来做的