# 缓存机制
缓存机制其实就是提前将一部分内容放入缓存中,下次获取数据时可以直接从缓存中读取,而不是再向数据库查询,提升效率。
Mybatis 存在一级缓存和二级缓存。
# 一级缓存
一级缓存无法关闭,只能调整:
try(SqlSession sqlSession = SqlUtil.getSqlSession()) { | |
Mapper mapper = sqlSession.getMapper(Mapper.class); | |
Student s1 = mapper.selectStudentBySid(1); | |
Student s2 = mapper.selectStudentBySid(1);// 通过 sid 查询学生 | |
System.out.println(s1 == s2); | |
} | |
// 结果为 true |
也就是说,s2 和 s1 指向的是同一片内存地址。如果你觉得・不明显,可以改一下构造函数
@Data | |
public class Student { | |
int sid; | |
String name; | |
public Student() { | |
System.out.println("Student被构造"); | |
} | |
} | |
// 结果是,最后只打印一次 ——Student 被构造 |
通过前面的学习得知 Mybatis 在映射为对象时,在只有一个构造方法的情况下,无论你构造方法写成什么样子,都会去调用一次构造方法,如果存在多个构造方法,那么就会去找匹配的构造方法。我们可以通过查看构造方法来验证对象被创建了几次。
但是一级缓存会在进行 DML 操作 —— 插入,删除,更新后失效,当然,也只是当前会话的缓存会失效,这其实并不是一个好的机制,尽管它做到了会话之间的隔离,但是会出现数据的不一致性(可见性问题)。
当前会话结束后,也会清理全部的缓存,因为已经不会再用到了。一级缓存只针对于单个会话,多个会话之间不相通。
其实在 MySQL 中(InnoDB 存储引擎),DML 操作也不是即时写入磁盘中,而是会在缓存中呆着,只不过这个机制涉及的知识更多,包括刷脏,flush 链表,LRU 链表,缓存池啥的。
# 二级缓存
扩展到所有会话都能使用,默认是关闭状态。开启二级缓存,需要在映射器 XML(就是 mapper.xml)中添加:
<?xml version="1.0" encoding="UTF-8" ?> | |
<!DOCTYPE mapper | |
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" | |
"http://mybatis.org/dtd/mybatis-3-mapper.dtd"> | |
<mapper namespace="TestMapper"> | |
<cache/> | |
<select id="selectStudent" resultType="com.cyan.entity.Student"> | |
select * from student | |
</select> | |
</mapper> |
放在某一个 mapper 里面,二级缓存的官方配置:
<cache | |
eviction="FIFO" | |
flushInterval="60000" | |
size="512" | |
readOnly="true"/> |
如果我们不希望某个方法开启二级缓存,可以使用 useCache
属性:
<select id="selectStudent" resultType="Student" useCache="false"> | |
select * from student | |
</select> |
flushCache
属性在 select
语句里面默认为 false
,在 DML
语句中默认为 true
。可以通过该属性来调整缓存清理情况。
查询数据库时的优先级:二级缓存 > 一级缓存 > 数据库。
# 一致性问题
单独的一个程序使用 Mybatis,如果操作得当,也许不会出现一致性问题。但是当多个程序(计算机)使用自己的 Mybatis 访问相同的数据库,就会导致严重的一致性问题。程序 A 正在处理一批查询请求(较长时间),程序 B 正在处理一批修改请求,A 使用 Mybatis 的缓存机制,A 的 Mybaits 并不知道 B 在修改数据库,仍然使用 A 的可能过期了的缓存。
这时候需要关闭 Mybatis 缓存来保证一致性:
<!-- 在 configuration 标签下,这是关闭二级缓存,如果为 false,就是开启二级缓存 --> | |
<settings> | |
<setting name="cacheEnabled" value="false"/> | |
</settings> | |
<!-- 还需要将 select 语句的 useCache 改为 false--> |
后面,我们会继续学习 Redis、Ehcache、Memcache 等缓存框架,通过使用这些工具,就能够很好地解决缓存一致性问题。
# 参考
https://www.yuque.com/qingkongxiaguang/javaweb/gn0syt#2abf3eaa
hxstrive.com/subject/mybatis/223.htm