MyBatis缓存机制
缓存 Cache:将数据记录在内存中,通过减少IO的⽅式,来提⾼程序的执⾏效率。
mybatis的缓存:将select语句的查询结果放到缓存(内存)当中,下⼀次还是这条select语句的话,直接从缓存中取,不再查数据库。⼀⽅⾯是减少了IO。另⼀⽅⾯不再执⾏繁琐的查找算法。效率⼤⼤提升。
- mybatis缓存只针对于DQL语句,也就是说缓存机制只对应select语句。

mybatis缓存包括:
- ⼀级缓存:将查询到的数据存储到SqlSession中。
- ⼆级缓存:将查询到的数据存储到SqlSessionFactory中。
- 集成其它第三⽅的缓存:⽐如EhCache【Java语⾔开发的】、Memcache【C语⾔开发的】 等。
一级缓存
一级缓存是会话级的缓存,是针对 SqlSession 对象的。
⼀级缓存默认是开启的。不需要做任何配置。
原理:只要使⽤同⼀个SqlSession对象执⾏同⼀条SQL语句,就会⾛缓存。
- 如果不使用连接池等技术,不保证获取的是同一个SqlSession对象,则不会走缓存。
不走一级缓存的情况:
- 不是同一个SqlSession对象
- 查询条件变化(即不是同一条SQL)
一级缓存失效的情况:
手动清空了一级缓存:
sqlSession.clearCache();。两次DQL之间,执行了增删改(DML)操作。
和表无关,只要执行了DML,就会自动清空一级缓存,导致一级缓存失效。
二级缓存
二级缓存是应用级的缓存,是针对 SqlSessionFactory 对象的。
使用二级缓存的前提条件:
<setting name="cacheEnabled" value="true">全局性地启用或禁用所有映射器配置⽂件中已配置的任何缓存。默认就是true,⽆需设置。
在需要使⽤⼆级缓存的 SqlMapper.xml⽂件中添加配置:
<cache />。作为一个启用二级缓存的标志。
使⽤⼆级缓存的实体类对象必须是可序列化的,也就是必须实现java.io.Serializable 接⼝。
只有当 SqlSession关闭或提交后,才会将该 SqlSession 对象中的一级缓存数据写入到二级缓存中,此时二级缓存才可用。
只要两次查询之间出现了增删改(DML)操作。⼆级缓存就会失效。【⼀级缓存也会失效】
<cache /> 的相关配置

eviction:指定从缓存中移除某个对象的淘汰算法。默认采⽤LRU策略。
a. LRU:Least Recently Used。最近最少使⽤。优先淘汰在间隔时间内使⽤频率最低的对象。(还有⼀种淘汰算法LFU,淘汰累计最不常⽤)
b. FIFO:First In First Out。⼀种先进先出的数据缓存器。先进⼊⼆级缓存的对象最先被淘汰。
c. SOFT:软引⽤。淘汰软引⽤指向的对象。具体算法和JVM的垃圾回收算法有关。
d. WEAK:弱引⽤。淘汰弱引⽤指向的对象。具体算法和JVM的垃圾回收算法有关。
flushInterval: ⼆级缓存的刷新时间间隔。单位毫秒。
如果没有设置。就代表不刷新缓存,只要内存⾜够⼤,⼀直会向⼆级缓存中缓存数据。除⾮执⾏了增删改。
readOnly:
a. true:多条相同的sql语句执⾏之后返回的对象是共享的同⼀个。性能好,但是多线程并发可能会存在安全问题。
b. false:多条相同的sql语句执⾏之后返回的对象是副本,调⽤了clone⽅法。性能⼀般,但安全。
size:设置⼆级缓存中最多可存储的java对象数量。默认值1024。
集成 EnCache
集成EhCache是为了代替mybatis⾃带的**⼆级缓存**。一级缓存是不能替代的。
mybatis对外提供了接⼝,也可以集成第三⽅的缓存组件。⽐如EhCache、Memcache等,都可以。
EhCache是Java写的。Memcache是C语⾔写的。所以mybatis集成EhCache较为常⻅。
集成步骤:
引入依赖
<!--ehcache需要slf4j的⽇志组件,log4j不好使--> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>1.2.11</version> <scope>test</scope> </dependency>在类的根路径下新建
echcache.xml:<?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"> <!--磁盘存储:将缓存中暂时不使⽤的对象,转移到硬盘,类似于Windows系统的虚拟内存--> <diskStore path="e:/ehcache"/> <!--defaultCache:默认的管理策略--> <!--eternal:设定缓存的elements是否永远不过期。如果为true,则缓存的数据始终有效,如果为false那么还要根据timeToIdleSeconds,timeToLiveSeconds判断--> <!--maxElementsInMemory:在内存中缓存的element的最⼤数⽬--> <!--overflowToDisk:如果内存中数据超过内存限制,是否要缓存到磁盘上--> <!--diskPersistent:是否在磁盘上持久化。指重启jvm后,数据是否有效。默认为false--> <!--timeToIdleSeconds:对象空闲时间(单位:秒),指对象在多⻓时间没有被访问就会失效。只对eternal为false的有效。默认值0,表示⼀直可以访问--> <!--timeToLiveSeconds:对象存活时间(单位:秒),指对象从创建到失效所需要的时间。只对eternal为false的有效。默认值0,表示⼀直可以访问--> <!--memoryStoreEvictionPolicy:缓存的3 种清空策略--> <!--FIFO:first in first out (先进先出)--> <!--LFU:Less Frequently Used (最少使⽤).意思是⼀直以来最少被使⽤的。缓存的元素有⼀个hit 属性,hit 值最⼩的将会被清出缓存--> <!--LRU:Least Recently Used(最近最少使⽤). (ehcache 默认值).缓存的元素有⼀个时间戳,当缓存容量满了,⽽⼜需要腾出地⽅来缓存新的元素的时候,那么现有缓存元素中时间戳离当前时间最远的元素将被清出缓存--> <defaultCache eternal="false" maxElementsInMemory="1000" overflowToDis k="false" diskPersistent="false" timeToIdleSeconds="0" timeToLiveSeconds="600" memoryStor eEvictionPolicy="LRU"/> </ehcache>修改 Mapper.xml 中的
<cache />,添加 type 属性:<!--表示采用如下类提供的二级缓存服务--> <cache type="org.mybatis.caches.ehcache.EhcacheCache"/>
集成后,日志信息会多出缓存命中率的信息,表示走缓存次数的占比:

image-20230612213902950
