• MyBatis(三)——二级缓存


    MyBatis二级缓存

    缓存,缓冲存储是MyBatis中用于数据优化提高我们程序执行效率的方式。

    • 一级缓存默认开启,缓存范围SqlSession会话
    • 二级缓存手动开启,属于范围Mapper Namespace

    缓存的范围

    在这里插入图片描述
    MyBatis中这两个命名空间中的所有对象都被存储到JVM的内存中。MyBatis存储对象本质就使用了Map键值对的结构来保存的。

    默认情况下SqlSession的缓存也就是一级缓存是开启的,SqlSession中进行的查询结果都会被保存下来,默认的存储周期都是存储在当前的sqlSession中,也就意味着每一个用户所得到的一级缓存可能都是不一样的。当这个会话被释放的时候存储在里面的一级缓存对象都会被清空。 作为SqlSession对象存储一级缓存时间是非常短的,也就意味着存储在这里的一级缓存使用率不高,也可能会浪费额外的存储空间。
    为了解决这个问题,MyBatis提供了二级缓存,二级缓存不属于SqlSession,它的作用范围更大,属于整个namespace命名空间,存储在二级缓存对象会被所有的SqlSession共享。

    二级缓存运行规则

    • 二级开启后默认所有查询操作均使用缓存
    • 写操作commit提交时对该namespace缓存强制清空
    • 配置useCache=false可以不用缓存
    • 配置flushCache=true代表强制清空缓存

    测试一级缓存

    @Test
        public void testLv1Cache() throws Exception {
            SqlSession session = null;
            try {
                session = MybatisUtils.openSession();
                RepositoryStock stock = session.selectOne("stock.selectById",20);
                RepositoryStock stock1 = session.selectOne("stock.selectById",20);
                System.out.println(stock.getMaterialName());
                System.out.println(stock.hashCode()+":"+stock1.hashCode());
            }catch (Exception e){
                throw e;
            }finally {
                MybatisUtils.closeSession(session);
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    调用了两次stock.selectById但是在日志中只打印了一个SQL,虽然得到两个变量但是指向的是相同的内存区域
    在这里插入图片描述

    中间加commit(),日志中打印出两条sql语句

        @Test
        public void testLv1Cache() throws Exception {
            SqlSession session = null;
            try {
                session = MybatisUtils.openSession();
                RepositoryStock stock = session.selectOne("stock.selectById",20);
                session.commit();
                RepositoryStock stock1 = session.selectOne("stock.selectById",20);
                System.out.println(stock.getMaterialName());
                System.out.println(stock.hashCode()+":"+stock1.hashCode());
            }catch (Exception e){
                throw e;
            }finally {
                MybatisUtils.closeSession(session);
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    在这里插入图片描述

    开启二级缓存

    在RepositoryMapper.xml中的< mapper>标签下添加标签

    <cache eviction="LRU" flushInterval="600000" size="512" readOnly="true"/>
    
    • 1

    测试代码

        @Test
        public void testLv2Cache() throws Exception {
            SqlSession session = null;
            try {
                session = MybatisUtils.openSession();
                RepositoryStock stock = session.selectOne("stock.selectById",20);
                System.out.println(stock.getMaterialName());
                System.out.println(stock.hashCode());
            }catch (Exception e){
                throw e;
            }finally {
                MybatisUtils.closeSession(session);
            }
            SqlSession session1 = null;
            try {
                session1 = MybatisUtils.openSession();
                RepositoryStock stock = session1.selectOne("stock.selectById",20);
                System.out.println(stock.getMaterialName());
                System.out.println(stock.hashCode());
            }catch (Exception e){
                throw e;
            }finally {
                MybatisUtils.closeSession(session1);
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    运行结果只执行了一次SQL,说明配置的二级缓存生效了
    Cache Hit Ratio [stock]: 0.5意思是对于[stock]这个对象,缓存的命中率为0.5
    在这里插入图片描述

    二级缓存配置

    cache二级缓存标签的属性

    eviction

    eviction是缓存的清除策略,当缓存对象数量达到上限后,自动触发对应算法对缓存对象清除

    1. LRU – 最近最久未使用:移除最长时间不被使用的对象。
      O1 O2 O3 O4 … O512
      14 99 83 1 893
    2. FIFO – 先进先出:按对象进入缓存的顺序来移除它们。
    3. SOFT – 软引用:移除基于垃圾收集器状态和软引用规则的对象。
    4. WEAK – 弱引用:更积极的移除基于垃圾收集器状态和弱引用规则的对象。

    flushInterval

    间隔多长时间自动清理缓存,单位:毫秒
    flushInterval=“600000” ,600000毫秒=10分钟
    每隔10分钟对缓存进行全局清除,这个时间可以设置时间长一点

    size

    缓存长度,最多可以缓存多少个对象,如果缓存的是实体类代表缓存一个对象,如果查询结果返回的是一个List集合的话也只会算作是一个对象。
    个人建议,在缓存中只需保留单个的实体对象缓存,不要去保留大量的List查询结果这样缓存命中率比较低。相对的如果是selectById根据id号返回唯一的对象,把他保存到缓存中的做法比较好。
    size的长度不要太小,比如当前的表有1200多条数据,那么在内存足够的情况下size应该大于1200,意味着所有库存实体对象都会在二级缓存中进行保存。这样做的好处就是,在按id进行查询时会直接从内存中提取,效率是非常非常高的。

    readOnly

    是否是只读的
    设置为true,代表返回只读缓存,每次从缓存取出的是缓存对象本身,这种执行效率较高
    设置为false,代表每次取出的是缓存对象的“副本”,每一次取出的对象都是不同的,这种安全性较高

    实际开发中大多设置为true就可以了

    其他标签中的缓存设置

    useCache

    是否使用缓存,对于selectAll查询所有的这种形式不推荐把查询结果放入缓存,因为全局查询的时候数据量实在是太大了。
    useCache="false"代表查询结果不被放入缓存

    <select id="selectAll" resultType="com.zl.mybatis.entity.RepositoryStock" useCache="false">
    
    • 1

    无论一级缓存二级缓存,当执行了commit()的时候当前的缓存都会被清空。但是在某些特定的需求场景下希望执行完insert语句立马清空缓存,而不是等待commit()执行完再清空。这时候可以使用flushCache=“true”,在SQL执行完以后立即清空缓存

    <insert id="insert" parameterType="com.zl.mybatis.entity.RepositoryStock" flushCache="true">
    
    • 1

    这个配置不只可以放到写数据语句中,也可以放到查询中。执行完这句SQL后缓存就会被清空,清空完缓存这条SQL的执行结果同样不会被放入缓存。

    <select id="selectStockMap" resultType="java.util.LinkedHashMap" flushCache="true">
    
    • 1
  • 相关阅读:
    【LeetCode刷题日志】225.用队列实现栈
    驱动开发:内核字符串拷贝与比较
    深入理解Java多线程之线程间的通信方式(上)
    Docker简介
    从零开始设计一个共识算法——一场没有硝烟的战争
    Charles无法打开导出的har文件解决方法
    【UNR #6 C】稳健型选手(分治)(主席树)(二分)
    基于51单片机酒精浓度检测仪超限报警Proteus仿真
    Asp-Net-Core开发笔记:给SwaggerUI加上登录保护功能
    简单聊聊运维监控的其他用途
  • 原文地址:https://blog.csdn.net/xfx_1994/article/details/126109583