美文网首页
Mybatis缓存——二级缓存

Mybatis缓存——二级缓存

作者: 程序员点点 | 来源:发表于2021-01-21 20:31 被阅读0次

    二级缓存存在于 SqlSessionFactory 生命周期中。

    2.1 配置二级缓存

    2.1.1 全局开关

    在 mybatis 中, 二级缓存有全局开关和分开关, 全局开关, 在 mybatis-config.xml 中如下配置:

    <settings>
      <!--全局地开启或关闭配置文件中的所有映射器已经配置的任何缓存。 -->
      <setting name="cacheEnabled" value="true"/>
    </settings>
    
    

    默认是为 true, 即默认开启总开关。

    2.1.2 分开关

    分开关就是说在 *Mapper.xml 中开启或关闭二级缓存, 默认是不开启的。

    2.1.3 entity 实现序列化接口

    public class Student implements Serializable {
    
        private static final long serialVersionUID = -4852658907724408209L;
    
        ...
    
    }
    
    

    2.2 使用二级缓存

    @Test
    public void secendLevelCacheTest() {
    
        // 获取 SqlSession 对象
        SqlSession sqlSession = sqlSessionFactory.openSession();
        //  获取 Mapper 对象
        StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);
        // 使用 Mapper 接口的对应方法,查询 id=2 的对象
        Student student = studentMapper.selectByPrimaryKey(2);
        // 更新对象的名称
        student.setName("奶茶");
        // 再次使用相同的 SqlSession 查询id=2 的对象
        Student student1 = studentMapper.selectByPrimaryKey(2);
        Assert.assertEquals("奶茶", student1.getName());
        // 同一个 SqlSession , 此时是一级缓存在作用, 两个对象相同
        Assert.assertEquals(student, student1);
    
        sqlSession.close();
    
        SqlSession sqlSession1 = sqlSessionFactory.openSession();
        StudentMapper studentMapper1 = sqlSession1.getMapper(StudentMapper.class);
        Student student2 = studentMapper1.selectByPrimaryKey(2);
        Student student3 = studentMapper1.selectByPrimaryKey(2);
        // 由于我们配置的 readOnly="true", 因此后续同一个 SqlSession 的对象都不一样
        Assert.assertEquals("奶茶", student2.getName());
        Assert.assertNotEquals(student3, student2);
    
        sqlSession1.close();
    }
    
    

    结果如下:

    2018-09-29 23:14:26,889 [main] DEBUG [org.apache.ibatis.datasource.pooled.PooledDataSource] - Created connection 242282810.
    2018-09-29 23:14:26,889 [main] DEBUG [org.apache.ibatis.transaction.jdbc.JdbcTransaction] - Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@e70f13a]
    2018-09-29 23:14:26,897 [main] DEBUG [com.homejim.mybatis.mapper.StudentMapper.selectByPrimaryKey] - ==>  Preparing: select student_id, name, phone, email, sex, locked, gmt_created, gmt_modified from student where student_id=? 
    2018-09-29 23:14:26,999 [main] DEBUG [com.homejim.mybatis.mapper.StudentMapper.selectByPrimaryKey] - ==> Parameters: 2(Integer)
    2018-09-29 23:14:27,085 [main] TRACE [com.homejim.mybatis.mapper.StudentMapper.selectByPrimaryKey] - <==    Columns: student_id, name, phone, email, sex, locked, gmt_created, gmt_modified
    2018-09-29 23:14:27,085 [main] TRACE [com.homejim.mybatis.mapper.StudentMapper.selectByPrimaryKey] - <==        Row: 2, 小丽, 13821378271, xiaoli@mybatis.cn, 0, 0, 2018-09-04 18:27:42.0, 2018-09-04 18:27:42.0
    2018-09-29 23:14:27,093 [main] DEBUG [com.homejim.mybatis.mapper.StudentMapper.selectByPrimaryKey] - <==      Total: 1
    2018-09-29 23:14:27,093 [main] DEBUG [com.homejim.mybatis.mapper.StudentMapper] - Cache Hit Ratio [com.homejim.mybatis.mapper.StudentMapper]: 0.0
    2018-09-29 23:14:27,108 [main] DEBUG [org.apache.ibatis.transaction.jdbc.JdbcTransaction] - Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@e70f13a]
    2018-09-29 23:14:27,116 [main] DEBUG [org.apache.ibatis.transaction.jdbc.JdbcTransaction] - Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@e70f13a]
    2018-09-29 23:14:27,116 [main] DEBUG [org.apache.ibatis.datasource.pooled.PooledDataSource] - Returned connection 242282810 to pool.
    2018-09-29 23:14:27,124 [main] DEBUG [com.homejim.mybatis.mapper.StudentMapper] - Cache Hit Ratio [com.homejim.mybatis.mapper.StudentMapper]: 0.3333333333333333
    2018-09-29 23:14:27,124 [main] DEBUG [com.homejim.mybatis.mapper.StudentMapper] - Cache Hit Ratio [com.homejim.mybatis.mapper.StudentMapper]: 0.5
    
    

    以上结果, 分几个过程解释:
    第一阶段:

    在第一个 SqlSession 中, 查询出 student 对象, 此时发送了 SQL 语句;
    student更改了name 属性;
    SqlSession 再次查询出 student1 对象, 此时不发送 SQL 语句, 日志中打印了 「Cache Hit Ratio」, 代表二级缓存使用了, 但是没有命中。 因为一级缓存先作用了。
    由于是一级缓存, 因此, 此时两个对象是相同的。
    调用了 sqlSession.close(), 此时将数据序列化并保持到二级缓存中。
    第二阶段:

    新创建一个 sqlSession.close() 对象;
    查询出 student2 对象,直接从二级缓存中拿了数据, 因此没有发送 SQL 语句, 此时查了 3 个对象,但只有一个命中, 因此 命中率 1/3=0.333333;
    查询出 student3 对象,直接从二级缓存中拿了数据, 因此没有发送 SQL 语句, 此时查了 4 个对象,但只有一个命中, 因此 命中率 2/4=0.5;
    由于 readOnly=“true”, 因此 student2 和 student3 都是反序列化得到的, 为不同的实例。

    2.3 配置详解

    查看 dtd 文件, 可以看到如下约束:

    <!ELEMENT cache (property*)>
    <!ATTLIST cache
    type CDATA #IMPLIED
    eviction CDATA #IMPLIED
    flushInterval CDATA #IMPLIED
    size CDATA #IMPLIED
    readOnly CDATA #IMPLIED
    blocking CDATA #IMPLIED
    >
    
    

    从中可以看出:

    1. cache 中可以出现任意多个 property子元素;
    2. cache 有一些可选的属性 type, eviction, flushInterval, size, readOnly, blocking.

    2.3.1 type

    type 用于指定缓存的实现类型, 默认是PERPETUAL, 对应的是 mybatis 本身的缓存实现类 org.apache.ibatis.cache.impl.PerpetualCache

    后续如果我们要实现自己的缓存或者使用第三方的缓存, 都需要更改此处。

    2.3.2 eviction

    eviction 对应的是回收策略, 默认为 LRU

    1. LRU: 最近最少使用, 移除最长时间不被使用的对象。

    2. FIFO: 先进先出, 按对象进入缓存的顺序来移除对象。

    3. SOFT: 软引用, 移除基于垃圾回收器状态和软引用规则的对象。

    4. WEAK: 弱引用, 移除基于垃圾回收器状态和弱引用规则的对象。

    2.3.3 flushInterval

    flushInterval 对应刷新间隔, 单位毫秒, 默认值不设置, 即没有刷新间隔, 缓存仅仅在刷新语句时刷新。

    如果设定了之后, 到了对应时间会过期, 再次查询需要从数据库中取数据。

    2.3.4 size

    size 对应为引用的数量,即最多的缓存对象数据, 默认为 1024

    2.3.5 readOnly

    readOnly 为只读属性, 默认为 false

    1. false: 可读写, 在创建对象时, 会通过反序列化得到缓存对象的拷贝。 因此在速度上会相对慢一点, 但重在安全。

    2. true: 只读, 只读的缓存会给所有调用者返回缓存对象的相同实例。 因此性能很好, 但如果修改了对象, 有可能会导致程序出问题。

    2.3.6 blocking

    blocking 为阻塞, 默认值为 false。 当指定为 true 时将采用 BlockingCache 进行封装。

    使用 BlockingCache 会在查询缓存时锁住对应的 Key,如果缓存命中了则会释放对应的锁,否则会在查询数据库以后再释放锁,这样可以阻止并发情况下多个线程同时查询数据。

    image.png
    2.4 注意事项
    由于在更新时会刷新缓存, 因此需要注意使用场合:查询频率很高, 更新频率很低时使用, 即经常使用 select, 相对较少使用delete, insert, update。

    缓存是以 namespace 为单位的,不同 namespace 下的操作互不影响。但刷新缓存是刷新整个 namespace 的缓存, 也就是你 update 了一个, 则整个缓存都刷新了。

    最好在 「只有单表操作」 的表的 namespace 使用缓存, 而且对该表的操作都在这个 namespace 中。 否则可能会出现数据不一致的情况。

    版权声明:本文为阿进的写字台原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。
    原文链接:https://blog.csdn.net/weixin_37139197/article/details/82908377

    相关文章

      网友评论

          本文标题:Mybatis缓存——二级缓存

          本文链接:https://www.haomeiwen.com/subject/ovjdzktx.html