缓存概述
定义
缓存是指可以进行高速数据交换的存储器,它先于内存与CPU交换数据,因此速率很快。L1 Cache(一级缓存)是CPU第一层高速缓存。内置的L1高速缓存的容量和结构对CPU的性能影响较大,不过高速缓冲存储器均由静态RAM组成,结构较复杂,在CPU管芯面积不能太大的情况下,L1级高速缓存的容量不可能做得太大。一般L1缓存的容量通常在32—256KB。L2 Cache(二级缓存)是CPU的第二层高速缓存,分内部和外部两种芯片。内部的芯片二级缓存运行速率与主频相同,而外部的二级缓存则只有主频的一半。L2高速缓存容量也会影响CPU的性能,原则是越大越好,普通台式机CPU的L2缓存一般为128KB到2MB或者更高,笔记本、服务器和工作站上用CPU的L2高速缓存最高可达1MB-3MB。由于高速缓存的速度越高价格也越贵,故有的计算机系统中设置了两级或多级高速缓存。紧靠内存的一级高速缓存的速度最高,而容量最小,二级高速缓存的容量稍大,速度也稍低。
缓存的工作原理
是当CPU要读取一个数据时,首先从CPU缓存中查找,找到就立即读取并送给CPU处理;没有找到,就从速率相对较慢的内存中读取并送给CPU处理,同时把这个数据所在的数据块调入缓存中,可以使得以后对整块数据的读取都从缓存中进行,不必再调用内存。正是这样的读取机制使CPU读取缓存的命中率非常高(大多数CPU可达90%左右),也就是说CPU下一次要读取的数据90%都在CPU缓存中,只有大约10%需要从内存读取。这大大节省了CPU直接读取内存的时间,也使CPU读取数据时基本无需等待。总的来说,CPU读取数据的顺序是先缓存后内存。
简单地说缓存就是把内存当中的一小块区域,把数据加载到这个区域,CPU可以重复利用,减少磁盘IO,减少查找数据的时间。
总结
缓存:存在于内存中的临时数据。
为什么使用缓存:减少和数据库的交互次数,提高执行效率。
适用于缓存的数据:经常查询并且不经常改变的、数据的正确与否对最终结果影响不大的。
不适用于缓存的数据:经常改变的数据、数据的正确与否对最终结果影响很大的。
例如:商品的库存,银行的汇率,股市的牌价。
Mybatis中的缓存
MyBatis包含一个非常强大的查询缓存特性,它可以非常方便地配置和定制。缓存可以极大的提升查询效率。
MyBatis系统中默认定义了两级缓存,一级缓存和二级缓存。
-
默认情况下,只有一级缓存( SqlSession级别的缓存,也称为本地缓存)开启。
-
二级缓存需要手动开启和配置,它是基于namespace级别的缓存。
-
为了提高扩展性。 MyBatis定义了缓存接口Cache。我们可以通过实现Cache接口来自定义二级缓存。
一级缓存
它指的是Mybatis中SqlSession对象的缓存。当我们执行查询之后,查询的结果会同时存入到SqlSession为我们提供一块区域中。该区域的结构是一个Map。当我们再次查询同样的数据,mybatis会先去sqlsession中查询是否有,有的话直接拿出来用。当SqlSession对象消失时,mybatis的一级缓存也就消失了。
-
一级缓存(local cache),即本地缓存,作用域默认为sqlSession。当 Session flush 或 close 后,该Session中的所有Cache将被清空。
-
本地缓存不能被关闭,但可以调用clearCache()来清空本地缓存,或者改变缓存的作用域。
-
在mybatis3.1之后,可以配置本地缓存的作用域。在 mybatis-config.xml中配置。
缓存命中:就是第一次执行的sql,查询的数据被加载到缓存区,再次执行相同的SQL,直接从内存当中的这块缓存区域读取的数据这一流程。
一级缓存失效的情况
同一次会话期间只要查询过的数据都会保存在当前SqlSession的一个Map中。
- key = hashCode + 查询的SqlId + 编写的sql查询语句 + 参数。
一级缓存失效的情况:
-
不同的SqlSession对应不同的一级缓存。
-
同一个SqlSession,但是查询条件不同。
-
同一个SqlSession,两次查询期间执行了任何一次增删改操作。
-
同一个SqlSession,两次查询期间手动清空了缓存。
-
事务回滚时也会删除一级缓存。
MyBatis集成Spring造成一级缓存失效问题
MyBatis集成Spring造成一级缓存失效,其实是Spring对SqlSession进行了封装,通过SqlSessionTemplate,使得每次调用Sql,都会重新创建一个SqlSession。一级缓存必须是同一会话才能命中,所以这些场景不能命中。
解决办法:给Spring添加事务即可,添加事务之后,SqlSessionInterceptor(会话拦截器)就会判断两次请求是否在同一事务当中,如果是就会用同一个SqlSession来解决。
二级缓存
二级缓存是 mapper 映射级别的缓存,它指的是Mybatis中SqlSessionFactory对象的缓存。由同一个SqlSessionFactory对象创建的SqlSession共享其缓存。
-
二级缓存(second level cache),全局作用域缓存。
-
二级缓存默认不开启,需要手动配置。
-
MyBatis提供二级缓存的接口以及实现,缓存实现要求 POJO实现Serializable接口。
-
二级缓存在SqlSession关闭或提交之后才会生效。
使用步骤:
1、让Mybatis框架支持二级缓存(全局配置文件 mybatis-config.xml 中开启二级缓存)。
<settings>
<setting name="cacheEnabled" value="true"/>
</settings>
2、让当前的映射文件支持二级缓存(mapper.xml中配置cache)。
<cache/>
cache标签的属性:
eviction:缓存的回收策略:
LRU – 最近最少使用的:移除最长时间不被使用的对象。
FIFO – 先进先出:按对象进入缓存的顺序来移除它们。
SOFT – 软引用:移除基于垃圾回收器状态和软引用规则的对象。
WEAK – 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。
默认的是 LRU。
flushInterval:缓存刷新间隔
缓存多长时间清空一次,默认不清空,设置一个毫秒值。
readOnly:是否只读:
true:只读;Mybatis认为所有从缓存中获取数据的操作都是只读操作,不会修改数据。
Mybatis为了加快获取速度,直接就会将数据在缓存中的引用交给用户。不安全,速度快。
false:非只读:Mybatis觉得获取的数据可能会被修改。
Mybatis会利用序列化/反序列的技术克隆一份新的数据给你。安全,速度慢。
size:缓存存放多少元素;
type="":指定自定义缓存的全类名;
实现Cache接口即可;
3、让当前的操作支持二级缓存(在select标签中配置 useCache="true"
)。
<!-- 根据 id 查询 -->
<select id="findById" resultType="user" parameterType="int" useCache="true">
select * from user where id = #{uid}
</select>
注意: 实体类需要实现Serializable接口。
缓存有关的设置以及属性
-
全局setting的cacheEnable:配置二级缓存的开关。一级缓存一直是打开的。
-
select标签的useCache属性:配置这个select是否使用二级缓存。一级缓存一直是使用的
-
每个增删改标签的flushCache属性:增删改默认flushCache=true。sql执行以后,会同时清空一级和二级缓存。查询默认flushCache=false。
-
sqlSession.clearCache():只是用来清除一级缓存。
-
全局setting的localCacheScope:本地缓存作用域:(一级缓存SESSION),当前会话的所有数据保存在会话缓存中;STATEMENT:可以禁用一级缓存。
缓存原理图示:
第三方缓存整合
EhCache是一个纯Java的进程内缓存框架,具有快速、精干等特点,是Hibernate中默认的CacheProvider。
MyBatis定义了Cache接口方便我们进行自定义扩展。
package org.apache.ibatis.cache;
import java.util.concurrent.locks.ReadWriteLock;
public interface Cache {
String getId();
void putObject(Object key, Object value);
Object getObject(Object key);
Object removeObject(Object key);
void clear();
int getSize();
ReadWriteLock getReadWriteLock();
}
MyBatis整合ehcache
步骤:
1、加入mybatis-ehcache依赖
<dependency>
<groupId>org.mybatis.caches</groupId>
<artifactId>mybatis-ehcache</artifactId>
<version>1.2.1</version>
</dependency>
2、编写ehcache.xml配置文件
属性说明:
-
diskStore:指定数据在磁盘中的存储位置。
-
defaultCache:当借助CacheManager.add(“demoCache”)创建Cache时,EhCache便会采用。
-
defalutCache:指定的的管理策略。
必须属性
-
maxElementsInMemory:在内存中缓存的element的最大数目 。
-
maxElementsOnDisk:在磁盘上缓存的element的最大数目,若是0表示无穷大。
- eternal:设定缓存的elements是否永远不过期。如果为true,则缓存的数据始终有效,如果为false那么还要根据timeToIdleSeconds,timeToLiveSeconds判断。
-
overflowToDisk:设定当内存缓存溢出的时候是否将过期的element缓存到磁盘上。
可选属性
-
timeToIdleSeconds:当缓存在EhCache中的数据前后两次访问的时间超过timeToIdleSeconds的属性取值时,这些数据便会删除,默认值是0,也就是可闲置时间无穷大。
-
timeToLiveSeconds:缓存element的有效生命期,默认是0.,也就是element存活时间无穷大。
-
diskSpoolBufferSizeMB:这个参数设置DiskStore(磁盘缓存)的缓存区大小.默认是30MB.每个Cache都应该有自己的一个缓冲区。
-
diskPersistent:在VM重启的时候是否启用磁盘保存EhCache中的数据,默认是false。
-
diskExpiryThreadIntervalSeconds:磁盘缓存的清理线程运行间隔,默认是120秒。每个120s,相应的线程会进行一次EhCache中数据的清理工作。
-
memoryStoreEvictionPolicy:当内存缓存达到最大,有新的element加入的时候, 移除缓存中element的策略。默认是LRU(最近最少使用),可选的有LFU(最不常使用)和FIFO(先进先出)。
<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">
<!-- 磁盘保存路径 -->
<diskStore path="D:\\ehcache" />
<defaultCache
maxElementsInMemory="10000"
maxElementsOnDisk="10000000"
eternal="false"
overflowToDisk="true"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU">
</defaultCache>
</ehcache>
3、配置cache标签<cache type="org.mybatis.caches.ehcache.EhcacheCache"></cache>
另外:
参照缓存: 若想在命名空间中共享相同的缓存配置和实例。可以使用 cache-ref 元素来引用另外一个缓存。
<mapper namespace="com.coydone.mapper.DepartmentMapper">
<!-- 引用缓存:namespace:指定和哪个名称空间下的缓存一样 -->
<cache-ref namespace="com.coydone.mapper.EmployeeMapper"/>
<mapper/>
评论区