简介

什么是缓存

**将一次查询的结果暂存至内存,后续查询只需查询缓存**

为什么使用缓存

**减少与数据库的交互次数,减少系统开销,提高系统效率**

什么样的数据能使用缓存

**经常查询且不常修改的数据**

Mybatis缓存

一级缓存

也叫本地缓存,默认开启,无法关闭,只在一次SqlSession中有效(拿到连接->关闭连接),底层由【Map】实现数据存储

  • 与数据库同一次会话期间查询到的数据会放在本地缓存中
  • 若之后需要获取相同数据,直接从缓存中获取
    <select id="queryUserById" resultType="User">
        SELECT * FROM user WHERE id = #{id};
    </select>

    <update id="updateUser" parameterType="User">
        UPDATE user set name = #{name}, pwd = #{pwd} WHERE id = #{id};
    </update>
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = mapper.queryUserById(1);
User user2 = mapper.queryUserById(1);

System.out.println(user == user2);
sqlSession.close();
  • 日志输出SQL语句仅执行一次,且输出结果为true,表明两个user实例对象为同一个
sqlSession.getMapper(UserMapper.class);
User user = mapper.queryUserById(1);

User iris = new User(1, "Iris", "123");
mapper.updateUser(iris);

User user2 = mapper.queryUserById(1);
System.out.println(user == user2);
  • 在修改数据库数据(哪怕不是目标记录)后再次查询会重新访问数据库

【缓存失效原因】

  • 查询不同的记录
  • 查询不同的Mapper
  • 不同线程查询
  • 增删改操作均可能导致缓存刷新
  • 手动清除

二级缓存

需在核心配置文件中打开缓存【即使默认开启】,才能手动配置缓存属性

开启全局缓存(手动显示开启)

<setting name="cacheEnabled" value="true"/>

全局缓存,由于一级缓存作用域太低从而产生,基于namespace级别的缓存,二级缓存会将每次会话的一次缓存出保存至二级缓存,不同mapper所查询的数据放在其对应缓存【Map】中
二级缓存由标签cache实现,其中可设置相关属性:

  • 缓存策略
    • LRU【默认】:最近最少使用(移除最长时间不被使用的对象)
    • FIFO:先进先出(按对象进入缓存的顺序来移除它们)
    • SOFT:软引用(基于垃圾回收器状态和软引用规则移除对象)
    • WEAK:弱引用(更积极地基于垃圾收集器状态和弱引用规则移除对象)
  • 缓存刷新间隔(ms)
  • 引用数目【默认为1024个对象】
  • 是否仅可读【默认可读写】
    下面的示例创建了一个FIFO缓存,间隔60刷新,最多存储512个对象且返回的对象仅可读
<cache
  eviction="FIFO"
  flushInterval="60000"
  size="512"
  readOnly="true"/>

测试二级缓存是否有效

    SqlSession sqlSession = MybatisUtils.getSqlSession();
    SqlSession sqlSession2 = MybatisUtils.getSqlSession();

    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    UserMapper mapper2 = sqlSession2.getMapper(UserMapper.class);

    User user = mapper.queryUserById(1);
    System.out.println(user);
    sqlSession.close();

    User user2 = mapper2.queryUserById(1);
    System.out.println(user2);
        
    System.out.println(user == user2);
    sqlSession2.close();

【结果输出:true】意为sqlSqssion2在sqlSession关闭后从二级缓存中获取到了sqlSession存入的数据。
报错【对象未序列】的解决方法

  • 实例对象继承Serializable
    public class User implements Serializable
  • 设置缓存对象仅可读(mapper.xml中)
    readOnly="true"

小结

  • 只要开启二级缓存,在相同Mapper下均有效
  • 所有数据都会先放在一级缓存中
  • 只有当会话提交/关闭,数据才转存至二级缓存中

Mybatis缓存原理

Mybatis学习笔记-缓存_数据