# 缓存机制

缓存机制其实就是提前将一部分内容放入缓存中,下次获取数据时可以直接从缓存中读取,而不是再向数据库查询,提升效率。

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