zoukankan      html  css  js  c++  java
  • Mybatis的缓存

    1、MyBatis 缓存的基本介绍

    缓存是一般的 ORM 框架都会提供的功能,目的就是提升查询的效率和减少数据库的压力。缓存是存在内存中的临时数据,将用户经常查询的数据放在缓存(内存)中,用户去查询数据就不用从磁盘上(关系型数据库文件)查询,从缓存中查询,从而提高查询效率,解决了高并发系统的性能问题。经常查询并且不经常改变的数据就可以使用缓存。

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

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

    2、一级缓存(本地缓存)

    一级缓存也叫本地缓存,一级缓存是默认开启的。

    同一个 SqlSession 对象, 在参数和 SQL 完全一样的情况下,只执行一次 SQL 语句(如果缓存没有过期)。只有在参数和 SQL 完全一样的情况下, 才会有这种情况。

    示例:

    下面示例需要开启 mybatis 日志才能看到效果。

    假设我们在一个Session中查询两次相同记录,此时除了第一次查询外,后面的查询都会直接使用缓存,而不会再去查询数据库。

    @Test
    public void test01() throws IOException {
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);
        SqlSession session = factory.openSession();
    
        UserDao userDao = session.getMapper(UserDao.class);
        User user = userDao.findUserById(1);
        
        System.out.println("=======分隔符=======");
    
        User user2 = userDao.findUserById(1);
        System.out.println(user == user2);
    }

    日志输出如下:

    可以看到,第一次查询发送了 SQL 语句, 后返回了结果;第二次查询没有发送 SQL 语句, 直接从内存中获取了结果。两次查询获取到的对象是相同的,这说明第二次查询是直接从缓存拿到的结果。

    2.1、一级缓存的特性

    一级缓存特性:

    • 在同一个 SqlSession 中,相同SQL并且参数相同才会命中缓存。在同一个 SqlSession 中,Mybatis 会把执行的 SQL 和参数通过算法生成缓存的键 key, 将键 key 和 SQL 查询的结果作为 value 值存放在一个 Map 中, 如果后续的键一样,即 sql 和参数意义, 则直接从 Map 中获取缓存数据
    • 不同的 SqlSession 之间的缓存是相互隔离的。例如查询一条 SQL 后又重新创建了一个 sqlsession 对象,则这两个对象之间的缓存是不共用的
    • 查询不同的 mapper 的 xml 配置文件的 sql 语句,缓存不共用
    • 任何的增删改( UPDATE、 INSERT、DELETE)语句都会清空缓存。
    • 可以在 mapper 文件的 select 标签中配置 flushCache=“true” ,如 <select id="selectByPrimaryKey" flushCache="true" ...>,这样每次查询都会将这个 sqlsession 中的所有缓存清空。或者手动调用清空缓存的方法 sqlSession.clearCache(); 也可清除缓存。

    3、二级缓存(全局缓存)

    二级缓存也叫全局缓存,一级缓存作用域太低了,所以诞生了二级缓存。二级缓存存在于 SqlSessionFactory 生命周期中。二级缓存需要手动开启和配置,它是基于namespace级别的缓存。

    缓存机制:会先命中二级缓存,二级缓存中没有再看一级缓存,都没有才去查数据库。

    3.1、使用二级缓存 

    在 mybatis 的配置文件 mybatis-config.xml 中添加 <setting name="cacheEnabled" value="true"/> 配置:

    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE configuration
            PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-config.dtd">
    <configuration>
    
        <settings>
            <!--配置日志-->
            <setting name="logImpl" value="STDOUT_LOGGING" />
            <!--全局地开启或关闭配置文件中的所有映射器已经配置的任何缓存。 -->
            <setting name="cacheEnabled" value="true"/>
        </settings>
    
        ...
    </configuration>

    在 mapper 的配置文件 *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">
    
    <mapper namespace="dao.UserDao">
        <!--
            在当前Mapper.xml中使用二级缓存。
            也可以不配置属性,直接使用 <cache />。下面配置中的属性说明如下:
                1、缓存的清除策略为 LRU(默认值),即最近最少使用:移除最长时间不被使用的对象。
                2、每隔 60 秒刷新
                3、最多可以存储结果对象或列表的 512 个引用
                4、返回的对象被认为是只读的,因此对它们进行修改可能会在不同线程中的调用者产生冲突
        -->
        <cache
            eviction="FIFO"
            flushInterval="60000"
            size="512"
            readOnly="true"/>
    
        <select id="findUserById" resultType="entity.User">
            select * from user where id = #{id}
        </select>
    </mapper>

    注意,如果是直接使用 <cache />,即不配置属性,此时实体类即 User 类需要实现序列化 Serializable 接口。

    测试代码:

    @Test
    public void test01() throws IOException {
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);
    
        SqlSession session = factory.openSession();
        UserDao userDao = session.getMapper(UserDao.class);
        User user = userDao.findUserById(1);
        //需要手动提交事务
        session.commit();
        session.close();
    
        System.out.println("=======分隔符=======");
    
        SqlSession session2 = factory.openSession();
        UserDao userDao2 = session2.getMapper(UserDao.class);
        User user2 = userDao2.findUserById(1);
    
        System.out.println(user == user2);
    }

    日志输出如下:

    可以看到,在同一个 mapper 配置文件中的 SQL 语句,即使是不同的 sqlsession 对象,仍然可以命中缓存。

    只有查询才有缓存,我们可以根据数据是否需要缓存来配置该条查询语句是否缓存:

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

    3.2、二级缓存的特性

    二级缓存的特性:

    • 开启了二级缓存后,同一个Mapper中的相同 SQL 相同参数的查询可以在二级缓存中拿到数据。

    • 查出的数据都会被默认先放在一级缓存中,只有会话提交或者关闭以后,一级缓存中的数据才会转到二级缓存中。

  • 相关阅读:
    跟我一起了解koa(四)
    快速定位隐蔽的sql性能问题及调优【转载】
    PV,UV,IP
    ActiveMQ的安全机制使用及其源代码分析 [转]
    ActiveMQ中的安全机制 [转]
    ESB、SOA、EAI异同【转】
    磁盘 I/O 性能监控指标和调优方法
    PLS-00306:错误解决思路
    浅谈PetShop之使用存储过程与PLSQL批量处理(附案例)
    关于SQLSQL Server的三值逻辑简析
  • 原文地址:https://www.cnblogs.com/wenxuehai/p/15357086.html
Copyright © 2011-2022 走看看