【9】Mybatis一级缓存【一级缓存失效的情况】、二级缓存

14、缓存

14.1、简介

1、什么是缓存 [ Cache ]?

  • 存在内存中的临时数据。
  • 将用户经常查询的数据放在缓存(内存)中,用户去查询数据就不用从磁盘上(关系型数据库数据文件)查询,从缓存中查询,从而提高查询效率,解决了高并发系统的性能问题。

2、为什么使用缓存?

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

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

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

14.2、Mybatis缓存

  • MyBatis包含一个非常强大的查询缓存特性,它可以非常方便地定制和配置缓存。缓存可以极大的提升查询效率。

  • MyBatis系统中默认定义了两级缓存:一级缓存二级缓存

    • 默认情况下,只有一级缓存开启。(SqlSession级别的缓存,也称为本地缓存)
    • 二级缓存需要手动开启和配置,他是基于namespace级别的缓存。
    • 为了提高扩展性,MyBatis定义了缓存接口Cache。我们可以通过实现Cache接口来自定义二级缓存

14.3、一级缓冲

一级缓存也叫本地缓存:

  • 与数据库同一次会话期间查询到的数据会放在本地缓存中。
  • 以后如果需要获取相同的数据,直接从缓存中拿,没必须再去查询数据库;

测试

    @Test
    public void getUserByID(){
    
    
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        User user1 = mapper.getUserByID(1);
        System.out.println(user1);
        System.out.println("=====================================================");
        User user2 = mapper.getUserByID(1);
        System.out.println(user2);
        System.out.println(user1==user2);
        sqlSession.close();
    }

在这里插入图片描述

根据同一个id查询同一个用户,有一级缓存,只进行了一次sql查询

一级缓存失效的四种情况

  1. sqlSession不同

    @Test
    public void testQueryUserById(){
          
          
       SqlSession session = MybatisUtils.getSession();
       SqlSession session2 = MybatisUtils.getSession();
       UserMapper mapper = session.getMapper(UserMapper.class);
       UserMapper mapper2 = session2.getMapper(UserMapper.class);
    
       User user = mapper.queryUserById(1);
       System.out.println(user);
       User user2 = mapper2.queryUserById(1);
       System.out.println(user2);
       System.out.println(user==user2);
    
       session.close();
       session2.close();
    }
    

    观察结果:发现发送了两条SQL语句!

在这里插入图片描述

结论:每个sqlSession中的缓存相互独立

  1. sqlSession相同,查询条件不同

    @Test
    public void testQueryUserById(){
          
          
       SqlSession session = MybatisUtils.getSession();
       UserMapper mapper = session.getMapper(UserMapper.class);
       UserMapper mapper2 = session.getMapper(UserMapper.class);
    
       User user = mapper.queryUserById(1);
       System.out.println(user);
       User user2 = mapper2.queryUserById(2);
       System.out.println(user2);
       System.out.println(user==user2);
    
       session.close();
    }
    

    观察结果:发现发送了两条SQL语句!很正常的理解

    在这里插入图片描述

    结论:当前缓存中,不存在这个数据

  2. sqlSession相同,两次查询之间执行了增删改操作!

        @Test
        public void updateUser(){
          
          
            SqlSession sqlSession = MybatisUtils.getSqlSession();
            UserMapper mapper = sqlSession.getMapper(UserMapper.class);
            User user1 = mapper.getUserByID(1);
            System.out.println(user1);
    
            Map<String, Object> map = new HashMap<String, Object>();
            map.put("pwd","555555");
            map.put("id",8);
            int i = mapper.updateUser(map);
            System.out.println(i);
    
            User user2 = mapper.getUserByID(1);
            System.out.println(user2);
            System.out.println(user1==user2);
            sqlSession.close();
        }
    

    观察结果:查询在中间执行了增删改操作后,重新执行查询sql语句

    在这里插入图片描述

    结论:因为增删改操作可能会对当前数据产生影响

  3. sqlSession相同,手动清除一级缓存

        @Test
        public void getUserByID(){
          
          
            SqlSession sqlSession = MybatisUtils.getSqlSession();
            UserMapper mapper = sqlSession.getMapper(UserMapper.class);
            User user1 = mapper.getUserByID(1);
            System.out.println(user1);
            System.out.println("=====================================================");
            sqlSession.clearCache();
            User user2 = mapper.getUserByID(1);
            System.out.println(user2);
            System.out.println(user1==user2);
            sqlSession.close();
        }
    

    在这里插入图片描述

14.4、二级缓存

  • 二级缓存也叫全局缓存,一级缓存作用域太低了,所以诞生了二级缓存

  • 基于namespace级别的缓存,一个名称空间,对应一个二级缓存;

  • 工作机制

    • 一个会话查询一条数据,这个数据就会被放在当前会话的一级缓存中;
    • 如果当前会话关闭了,这个会话对应的一级缓存就没了;但是我们想要的是,会话关闭了,一级缓存中的数据被保存到二级缓存中;
    • 新的会话查询信息,就可以从二级缓存中获取内容;
    • 不同的mapper查出的数据会放在自己对应的缓存(map)中;

使用步骤

  1. 在核心配置文件mybatis-config中开启全局缓存

    <setting name="cacheEnabled" value="true"/>
    
  2. 在Mapper.xml映射文件中配置二级缓存

    <cache/>
    <select id="getUserByID" parameterType="int" resultType="User" useCache="true">
        select * from users where uid = #{id}
    </select>
    

    官方文档中:

    <cache
     eviction="FIFO"
     flushInterval="60000"
     size="512"
     readOnly="true"/>
    

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

  3. 代码测试

    @Test
    public void SecondCache(){
          
          
        SqlSession sqlSession1 = MybatisUtils.getSqlSession();
        SqlSession sqlSession2 = MybatisUtils.getSqlSession();
    
        UserMapper mapper1 = sqlSession1.getMapper(UserMapper.class);
        UserMapper mapper2 = sqlSession2.getMapper(UserMapper.class);
        User user1 = mapper1.getUserByID(1);
        System.out.println(user1);
        sqlSession1.close();
        System.out.println("==========================================================");
        User user2 = mapper2.getUserByID(1);
        System.out.println(user2);
        sqlSession2.close();
    }
    

    看一下效果:

    在这里插入图片描述

    可以看到,在第一个会话关闭后,将一级缓存放到二级缓存中,第二个会话查询相同数据的时候直接从二级缓存中取出来

小结

  • 只要开启了二级缓存,在同一个Mapper下就生效
  • 所有的数据都会先放在一级缓存中
  • 只有当会话提交,或者关闭的时候,才会提交到二级缓存中

14.5、缓存原理图

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_43215322/article/details/109565382