Mybatis缓存


前言

1. 什么是缓存[ Cache ]?

  • 有时候,某些数据是会经常需要访问的,像硬盘内部的缓存(暂存器的一种)会将读取比较频繁的一些数据存储在缓存中,再次读取时就可以直接从缓存中直接传输。(存在内存中的临时数据)
  • 将用户经常查询的数据放在缓存(内存)中,用户去查询数据就不用从磁盘上(关系型数据库数据文件)查询,从缓存中查询,从而提高查询效率,解决了高并发系统的性能问题。

2. 为什么使用缓存?

  • 减少和数据库的交互次数,减少系统开销,提高系统效率。

3. 什么样的数据能使用缓存?

  • 经常查询并且不经常改变的数据。

4. Mybatis缓存

  • MyBatis包含一个非常强大的查询缓存特性,它可以非常方便地定制和配置缓存。缓存可以极大的提升查询效率。
  • MyBatis系统中状认定义了两级缓存:一级缓存二级缓存
  • 默认情况下,只有一级缓存开启。(SqlSession级别的缓存;也称为本地缓存)
  • 二级缓存需要手动开启和配置,他是基于namespace级别的缓存。
  • 为了提高扩展性,MyBatis定义了缓存接口Cache。我们可以通过实现Cache接口来自定义二级缓存

一、Mybatis一级缓存

  • Mybatis的一级缓存在SqlSession上,只要通过SqlSession查过的数据,都会放在SqlSession上,下一次再查询相同id的数据,都直接冲缓存中取出来,而不用到数据库里去取了。
  • 与数据库同一次会话期间查询到的数据会放在本地缓存中。
  • 以后如果需求获取相同的数据,直接从缓存中拿,没必要再去查询数据库。

1.代码测试:

(1)Mapper接口

package dao.userdao;

import dao.pojo.User;
import org.apache.ibatis.annotations.Param;

public interface UserMapper {

    //通过id查询用户
    User getUserById(@Param("id") int id);
}

(2)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">
<!--namespace=绑定一个对应的dao接口/mapper接口-->
<mapper namespace="dao.userdao.UserMapper">

    <select id="getUserById" resultType="user">
        select * from user where id = #{id}
    </select>

</mapper>

(3)测试类

     @Test
    public void getUserById(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);

        User user = mapper.getUserById(1);

        System.out.println(user);
        System.out.println("===================================");
        User user2 = mapper.getUserById(1);

        System.out.println(user2);
        System.out.println(user==user2);

        sqlSession.close();
    }

在这里插入图片描述

2.小结

在这里插入图片描述

  • 观察代码和日志的对应,我可以看到只进行了一次SQL查询,而我们第二次查询结果是来自第一次查询的缓存。
  • 一级缓存默认是开启的,只在一次SqlSession中有效,也就是拿到连接到关闭连接的这个区间段。
  • 一级缓存相当与一个Map。
  • 如果SqlSession调用了close()方法,会释放掉一级缓存PerpetualCache对象,一级缓存将不可用。
  • 使用一级缓存的时候,因为缓存不能跨会话共享,不同的会话之间对于相同的数据可能有不一样的缓存。在有多个会话或者分布式环境下,会存在脏数据的问题。如果要解决这个问题,就要用到二级缓存。MyBatis 一级缓存(MyBaits 称其为 Local Cache)无法关闭。

3.一级缓存失效的情况:

(1)查询不同的东西。
(2)增删改操作,可能会改变原来的数据,所以会必定刷新缓存。
(3)查询不到的Mapper.xml
(4)手动清理缓存:sqlSession.clearCache();

二、Mybatis二级缓存

  • 二级缓存是用来解决一级缓存不能跨会话共享的问题的,范围是namespace 级别的,可以被多个SqlSession 共享(只要是同一个接口里面的相同方法,都可以共享),生命周期和应用同步。
  • 工作机制:
    (1)一个会话查询一条数据,这个数据就会被放在当前会话的一级缓存中;
    (2)如果当前会诺关闭了,这个会话对应的一级缓存就没了;会话关闭了,一级缓存中的数据被保存到二级缓存中;
    (3)新的会话查询信息,就可以从二级缓存中获取内容;
    (4)不同的mapper查出的数据会放在自己对应的缓存(map)中;

代码测试

(1)开启二级缓存

  • 核心配置文件中setting
    <settings>
        <!--标准日志输出-->
        <setting name="logImpl" value="STDOUT_LOGGING"/>
        <!--开启二级缓存-->
        <setting name="cacheEnabled" value="true"/>
    </settings>
  • Mapper.xml
    <!--在当前Mapper.xml中使用二级缓存-->
    <cache/>

也可以自定义参数:

    <!--在当前Mapper.xml中使用二级缓存-->
    <cache eviction="FIFO"
           flushInterval="60000"
           size="512"
           readOnly="true"/>

这个更高级的配置创建了一个 FIFO 缓存,每隔 60 秒刷新,最多可以存储结果对象或列表的 512 个引用,而且返回的对象被认为是只读的,因此对它们进行修改可能会在不同线程中的调用者产生冲突。

可用的清除策略有:

  • LRU – 最近最少使用:移除最长时间不被使用的对象。

  • FIFO – 先进先出:按对象进入缓存的顺序来移除它们。

  • SOFT – 软引用:基于垃圾回收器状态和软引用规则移除对象。

  • WEAK – 弱引用:更积极地基于垃圾收集器状态和弱引用规则移除对象。
    默认的清除策略是 LRU。

  • flushInterval(刷新间隔)属性可以被设置为任意的正整数,设置的值应该是一个以毫秒为单位的合理时间量。 默认情况是不设置,也就是没有刷新间隔,缓存仅仅会在调用语句时刷新。

  • size(引用数目)属性可以被设置为任意正整数,要注意欲缓存对象的大小和运行环境中可用的内存资源。默认值是 1024。

  • readOnly(只读)属性可以被设置为 true 或 false。只读的缓存会给所有调用者返回缓存对象的相同实例。 因此这些对象不能被修改。这就提供了可观的性能提升。而可读写的缓存会(通过序列化)返回缓存对象的拷贝。 速度上会慢一些,但是更安全,因此默认值是 false。

(2)测试代码

沿用一级缓存定义的接口和xml实现,记住Mapper.xml中开启二级缓存

测试类:

package dao.userdao;

import dao.pojo.User;
import dao.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;

public class UserMapperTest {

    @Test
    public void getUserById(){
//        创建两个SqlSession对象
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        SqlSession sqlSession2 = MybatisUtils.getSqlSession();

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

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

        User user = mapper.getUserById(1);

        System.out.println(user);
        sqlSession.close();
        System.out.println("===================================");

        User user2 = mapper2.getUserById(1);

        System.out.println(user2);

        sqlSession2.close();
    }
}

在这里插入图片描述
很明显,我们第二次查询的结果来自二级缓存

(3)小结

在这里插入图片描述

  • 只有当会话提交,或者关闭时,才会提交到二级缓存中,上图是第一个SqlSession 对象关闭,由于开启了二级缓存,所以数据会存放到二级缓存中,第二次查询得到的数据来自二级缓存。
  • 所有数据都会先放着一级缓存中
  • 只要开启了二级缓存,在同一个Mapper下就有效。
  • 注意:我们的实体类需要序列化,不然会报错:org.apache.ibatis.cache.CacheException: Error serializing object. Cause: java.io.NotSerializableException: dao.pojo.User
  • 序列化:实体类继承接口implements Serializable

三、Mybatis缓存原理

在这里插入图片描述

  • 用户查询数据顺序:先是二级缓存,其次一级缓存,如果一级和二级缓存都没有的话就查询数据库。
  • 上图一级缓存,每个SqlSession对象查询到的都会放入一级缓存,再次查询的结果来自一级缓存中,作用域是单独的SqlSession,注意缓存失效的几种情况。
  • 二级缓存是SqlSession关闭后,一级缓存的数据会存入二级缓存,作用域为整个Mapper.xml。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值