视频教程_免费高速下载|百度网盘-分享无限制 (baidu.com)
MyBatis 有一套灵活而强大的缓存机制,主要分为两级缓存:一级缓存(本地缓存)和二级缓存(全局缓存)。
-
一级缓存(本地缓存):
-
一级缓存是 MyBatis 默认开启的缓存,它指的是在同一个 SqlSession 中执行相同 SQL 语句时,第一次查询的结果会被缓存起来,后续再执行相同的查询,直接从缓存中获取结果,而不再去查询数据库。
-
一级缓存是基于 SqlSession 的生命周期的,当 SqlSession 关闭时,一级缓存也就失效了。
-
一级缓存可以通过清空缓存或提交事务来手动刷新,默认情况下,只要 SqlSession执行一条语句,就会刷新,不会等到 SqlSession结束(不像二级缓存)
-
一级缓存,在进行DML操作后,会使得缓存失效
-
一个会话DML操作只会重置当前会话的缓存,不会重置其他会话的缓存,也就是说,其他会话缓存是不会更新的!
-
-
二级缓存(全局缓存):
-
二级缓存是全局缓存,可以跨多个 SqlSession 共享缓存,但是只有当一个SqlSession结束之后会刷新二级缓存。所以当 SqlSession1和 SqlSession2都没结束且查询同样内容的时候,不会调用二级缓存,因为二级缓存啥都没有。
-
二级缓存需要在 MyBatis 的配置文件中进行配置启用,并且需要在映射文件中指定哪些语句使用二级缓存。
不希望某个方法开启缓存呢?我们可以添加useCache属性来关闭缓存:
<select id="getStudentBySid" resultType="Student" useCache="false"> select * from student where sid = #{sid} </select>
-
对于同一个 namespace 下的语句,如果开启了二级缓存,它们的查询结果会被缓存在一个全局的缓存区域中,当其他 SqlSession 执行相同的查询时,可以从全局缓存中获取结果。
-
二级缓存是基于 namespace 级别的,不同的 namespace 有独立的缓存区域,它们之间不会相互影响。
-
二级缓存默认是开启的,但需要在映射文件中明确指定
<cache/>
元素,才会对该 namespace 启用缓存。<mapper namespace="com.example.BlogMapper"> <cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/> <!-- 其他映射语句 --> </mapper>
namespace="com.example.BlogMapper":指定缓存的 namespace,即当前映射文件所属的命名空间。这个命名空间用于标识二级缓存的作用范围,是一个唯一的标识符,通常与映射器接口或者类的全路径名相对应。eviction="FIFO":指定缓存的淘汰策略。在这里,
FIFO
表示采用先进先出(First In, First Out)策略,即最先进入缓存的数据最先被淘汰。flushInterval="60000":指定缓存刷新的时间间隔,单位为毫秒。在这里,设置为 60000 毫秒,即 60 秒,表示 MyBatis 会定期刷新缓存,清理过期的缓存项。size="512":指定缓存的最大项数。当缓存中的项数达到设定的值时,MyBatis 会尝试清理缓存。readOnly="true":指定是否只读。设置为true
时,表示缓存只读,不会修改缓存中的数据,可以提高性能。默认值为false
。readOnly="true":指定是否只读。设置为true
时,表示缓存只读,不会修改缓存中的数据,可以提高性能。默认值为false
。 -
二级缓存的实现是可插拔的,可以自定义缓存实现,如使用 Ehcache、Redis 等。
-
添加了二级缓存之后,会先从二级缓存中查找数据,当二级缓存中没有时,才会从一级缓存中获取,当一级缓存中都还没有数据时,才会请求数据库
-
需要注意的是,缓存机制虽然能提高查询性能,但在一些更新操作(例如插入、更新、删除)时,可能会导致缓存的数据不一致。因此,在执行这类操作时,MyBatis 会自动清空相关的缓存,保证数据的正确性。开发者也可以通过手动清理缓存来控制缓存的行为。
虽然缓存机制给我们提供了很大的性能提升,但是缓存存在一个问题,我们之前在计算机组成原理
中可能学习过缓存一致性问题,也就是说当多个CPU在操作自己的缓存时,可能会出现各自的缓存内容不同步的问题,而Mybatis也会这样,我们来看看这个例子:
public static void main(String[] args) throws InterruptedException {
try (SqlSession sqlSession = MybatisUtil.getSession(true)){
TestMapper testMapper = sqlSession.getMapper(TestMapper.class);
while (true){
Thread.sleep(3000);
System.out.println(testMapper.getStudentBySid(1));
}
}
}
我们现在循环地每三秒读取一次,而在这个过程中,我们使用IDEA手动修改数据库中的数据,将1号同学的学号改成100.
结果是依小明的sid并没有发生改变,这也证明了Mybatis的缓存在生效,因为我们是从外部进行修改,Mybatis不知道我们修改了数据,所以依然在使用缓存中的数据,但是这样很明显是不正确的,因此,如果存在多台服务器或者是多个程序都在使用Mybatis操作同一个数据库,并且都开启了缓存,需要解决这个问题,要么就得关闭Mybatis的缓存来保证一致性:
<settings>
<setting name="cacheEnabled" value="false"/>
</settings>
cacheEnabled
是一个全局开关,用于控制是否开启缓存。在这里,将其值设置为 false
,表示禁用缓存。
<select id="getStudentBySid" resultType="Student" useCache="false" flushCache="true">
select * from student where sid = #{sid}
</select>
-
useCache="false"
:这个属性用于指定是否使用缓存。在这里,useCache
被设置为false
,表示禁用缓存。即使全局缓存配置是启用的,这个查询也不会使用缓存。 -
flushCache="true"
:这个属性用于指定是否刷新缓存。在这里,flushCache
被设置为true
,表示在执行这个查询之前会刷新(清空)缓存。即使全局缓存是启用的,这个查询会强制刷新缓存,确保获取最新的结果。
要么就需要实现缓存共用,也就是让所有的Mybatis都使用同一个缓存进行数据存取,在后面,我们会继续学习Redis、Ehcache、Memcache等缓存框架,通过使用这些工具,就能够很好地解决缓存一致性问题