【MyBatis12】缓存

1 缓存的概念

在增删改查四个操作中,以查询操作最为耗时且为用户发起频次最高的请求,因此如果能提升查询的性能,那么整个系统的性能也会得到较大幅度的提升。缓存便是提升查询性能的强有力的手段,并且在项目开发中,缓存是我们必备的一个组件。

缓存(Cache)是内存中的一块存储空间,服务于某个应用程序,旨在将频繁堆区的数据临时保存在内存中,便于二次快速访问。

  • 如果项目中没有缓存:在用户访问相同数据时,需要发起多次对数据库的直接访问,产生大量IO、读写磁盘的操作,效率低下。

    • Client发送数据到Server,Server需要直接和DB进行通信

    • 期间将会产生大量的本地IO、网络IO

    • 最终将会导致整个查询过程相当缓慢

  • 如果项目中存在缓存:首次访问时,查询数据库,将数据存储到缓存中;再次访问时(缓存命中),直接访问缓存,减少IO、磁盘的读写次数,提高效率。
    • 如果有一部分数据是需要频繁地被查询,而很少被修改, 那么这部分数据就适合拿出来放到缓存中去

    • 反而言之,如果一部分数据需要被频繁修改,那么就没有必要放到缓存中去

2 MyBatis一级缓存

MyBatis中的一级缓存是SqlSession级别的缓存:

  • 同一个SqlSession内的发起多次同构查询,会将数据保存在一级缓存中。

  • 一级缓存无需做任何配置,是默认开启的。

在MyBatis工具类中加入获得不同的SqlSession的方法来进行测试:

    //创建SqlSession(不同的SqlSession)
    public static SqlSession getSession() {
        return sqlSessionFactory.openSession();
    }

测试方法:

 

        SqlSession session1 = MyBatisUtil.getSession();
        SqlSession session2 = MyBatisUtil.getSession();

        UserDao session1Mapper = session1.getMapper(UserDao.class);
        UserDao session2Mapper = session2.getMapper(UserDao.class);

        User user1 = session1Mapper.queryUserById(2);
        User user1_1 = session1Mapper.queryUserById(2);
        System.out.println("==========================");
        User user2 = session2Mapper.queryUserById(2);

测试结果

Opening JDBC Connection
Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@e720b71]
==>  Preparing: select id,username,password,gender,regist_time from t_user where id=? 
==> Parameters: 2(Integer)
<==    Columns: id, username, password, gender, regist_time
<==        Row: 2, TestUpdate1111, TestUpdate, 1, 2022-07-31 01:06:03.0
<==      Total: 1
==========================
Opening JDBC Connection
Created connection 681384962.
Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@289d1c02]
==>  Preparing: select id,username,password,gender,regist_time from t_user where id=? 
==> Parameters: 2(Integer)
<==    Columns: id, username, password, gender, regist_time
<==        Row: 2, TestUpdate1111, TestUpdate, 1, 2022-07-31 01:06:03.0
<==      Total: 1

可以看到三次同样的查询,被==========================分割的前半部分由于在同一个SqlSession中,只进行了一次数据库通信;而被==========================分割的后半部分虽然和前两次查询的查询语句一样,但由于属于不同的SqlSession,所以需要进行两次数据库通信。

 

3 MyBatis二级缓存

MyBatis中的二级缓存是SqlSessionFactory级别的缓存:

  • 同一个SqlSessionFactory构建的SqlSession发起的多次同构查询,会将数据保存在二级缓存中。

    • 基本上在一个项目中只需要一个SqlSessionFactory对象,所以缓存在SqlSessionFactory级别的数据基本上是整个项目共享的

    • 缓存共享的周期更长,二级缓存更具有实用价值

  • sqlSession.commit()sqlSession.close()之后生效

在MyBatis配置文件中开启缓存:

mybatis-config.xml:

    <settings>
        ......
        <setting name="cacheEnabled" value="true"/>
        ......
    </settings>
  • 此标签可有可无,默认value是true

    • MyBatis中二级缓存默认开启

Mapper.xml:

<mapper namespace="com.qianglj.test1.dao.UserDao">
    <cache/>
    ......
  • 如果需要开启二级缓存,此标签不可省略

    • 不是所有的查询结果都会进入二级缓存

    • 只有在Mapper.xml中存在<cache/>标签的才会进入二级缓存

实现二级缓存,实体类需要实现java.io.Serializable;接口,否则会报如下错误:

org.apache.ibatis.cache.CacheException: Error serializing object.  Cause: java.io.NotSerializableException: com.qianglj.test1.pojo.User

实现了java.io.Serializable;以后进行测试,测试方法:

 

        SqlSession session1 = MyBatisUtil.getSession();
        SqlSession session2 = MyBatisUtil.getSession();

        UserDao session1Mapper = session1.getMapper(UserDao.class);
        UserDao session2Mapper = session2.getMapper(UserDao.class);

        session1Mapper.queryUserById(2);
        session1.close();
        System.out.println("==========================");
        session2Mapper.queryUserById(2);
        session2.close();
  • session1.close();

    • 必须明确的去关闭session

    • 关闭session意味着一个session中的所有操作都已经结束了,此时MyBatis才会把这个session中所查询到的数据缓存到二级缓存中

    • 如果不关闭的话,是不会进行缓存的

测试结果:

Cache Hit Ratio [com.qianglj.test1.dao.UserDao]: 0.0
Opening JDBC Connection
Created connection 1053631449.
Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@3ecd23d9]
==>  Preparing: select id,username,password,gender,regist_time from t_user where id=? 
==> Parameters: 2(Integer)
<==    Columns: id, username, password, gender, regist_time
<==        Row: 2, TestUpdate1111, TestUpdate, 1, 2022-07-31 01:06:03.0
<==      Total: 1
Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@3ecd23d9]
Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@3ecd23d9]
Returned connection 1053631449 to pool.
==========================
Cache Hit Ratio [com.qianglj.test1.dao.UserDao]: 0.5
  • 两个不同的sqlSession,只进行了一次连接

    • 第二次查询使用到了缓存

  • Cache Hit Ratio [com.qianglj.test1.dao.UserDao]: 0.5

    • 缓存命中率50%,第一次查询没有在缓存中找到数据,第二次找到了,所以缓存命中率是50%

  • 如果再做一次查询:

        SqlSession session1 = MyBatisUtil.getSession();
        SqlSession session2 = MyBatisUtil.getSession();
        SqlSession session3 = MyBatisUtil.getSession();

        UserDao session1Mapper = session1.getMapper(UserDao.class);
        UserDao session2Mapper = session2.getMapper(UserDao.class);
        UserDao session3Mapper = session3.getMapper(UserDao.class);

        session1Mapper.queryUserById(2);
        session1.close();
        System.out.println("==========================");
        session2Mapper.queryUserById(2);
        session2.close();
        System.out.println("==========================");
        session3Mapper.queryUserById(2);
        session3.close();

查询结果:

Cache Hit Ratio [com.qianglj.test1.dao.UserDao]: 0.0
Opening JDBC Connection
Created connection 1053631449.
Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@3ecd23d9]
==>  Preparing: select id,username,password,gender,regist_time from t_user where id=? 
==> Parameters: 2(Integer)
<==    Columns: id, username, password, gender, regist_time
<==        Row: 2, TestUpdate1111, TestUpdate, 1, 2022-07-31 01:06:03.0
<==      Total: 1
Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@3ecd23d9]
Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@3ecd23d9]
Returned connection 1053631449 to pool.
==========================
Cache Hit Ratio [com.qianglj.test1.dao.UserDao]: 0.5
==========================
Cache Hit Ratio [com.qianglj.test1.dao.UserDao]: 0.6666666666666666
    • Cache Hit Ratio [com.qianglj.test1.dao.UserDao]: 0.6666666666666666第三次也命中了,所以命中率变成了66.6666%
  • 如果同一个Mapper中缓存的数据进行了一次DML操作,MyBatis会自动地进行缓存清空并重新缓存

 

版权声明:
作者:jackqiang
链接:http://www.jackqiang.com/framework/mybatis/1950/cache/
来源:JackQiang's
文章版权归作者所有,未经允许请勿转载。

THE END
分享
二维码
< <上一篇
下一篇>>
文章目录
关闭
目 录