侧边栏壁纸
博主头像
coydone博主等级

记录学习,分享生活的个人站点

  • 累计撰写 306 篇文章
  • 累计创建 51 个标签
  • 累计收到 0 条评论

目 录CONTENT

文章目录

Mybatis缓存

coydone
2022-03-10 / 0 评论 / 0 点赞 / 315 阅读 / 5,843 字 / 正在检测是否收录...
温馨提示:
本文最后更新于 2022-04-10,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

缓存概述

定义

缓存是指可以进行高速数据交换的存储器,它先于内存与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包含一个非常强大的查询缓存特性,它可以非常方便地配置和定制。缓存可以极大的提升查询效率。

MyBatis系统中默认定义了两级缓存,一级缓存和二级缓存。

  1. 默认情况下,只有一级缓存( SqlSession级别的缓存,也称为本地缓存)开启。

  2. 二级缓存需要手动开启和配置,它是基于namespace级别的缓存。

  3. 为了提高扩展性。 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查询语句 + 参数。

一级缓存失效的情况:

  1. 不同的SqlSession对应不同的一级缓存。

  2. 同一个SqlSession,但是查询条件不同。

  3. 同一个SqlSession,两次查询期间执行了任何一次增删改操作。

  4. 同一个SqlSession,两次查询期间手动清空了缓存。

  5. 事务回滚时也会删除一级缓存。

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接口。

缓存有关的设置以及属性

  1. 全局setting的cacheEnable:配置二级缓存的开关。一级缓存一直是打开的。

  2. select标签的useCache属性:配置这个select是否使用二级缓存。一级缓存一直是使用的

  3. 每个增删改标签的flushCache属性:增删改默认flushCache=true。sql执行以后,会同时清空一级和二级缓存。查询默认flushCache=false。

  4. sqlSession.clearCache():只是用来清除一级缓存。

  5. 全局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/>
0

评论区