一级缓存&二级缓存

一级缓存

目标

  1. 为什么要缓存
  2. 一级缓存的使用

缓存概述

为什么要使用缓存

如果每次访问数据库都去获取连接,查询数据库的记录,返回结果集,效率比较低。

缓存相当于服务器内存中一块区域,会缓存最近查询的SQL语句以及sql语句的查询结果,如果再次发送相同的SQL语句,mybatis就不再重新访问数据库,而是从内存中读取已经存在的数据返回给客户端。

使用缓存的目的:就是为了提升查询的速度

缓存的分类

在mybatis中缓存分成2类:

  1. 一级缓存
  2. 二缓缓存

缓存结构

  1. 一级缓存是默认打开的,会话级别缓存,只在一个会话中起作用。
  2. 二级缓存必须是手动开启,可以在各个会话之间共享数据

image-20230524172144589

案例:一级缓存

一级缓存的范围:只在同一个会话中起作用,默认是自动开启的。

需求

通过同一个 sqlSession 对象,通过id查询2次,观察发出 sql 语句的次数。

步骤

  1. 在同一个测试方法中查询2次
  2. 输出用户信息

代码

package com.tyhxzy.test;

import com.tyhxzy.dao.UserMapper;
import com.tyhxzy.entity.User;
import com.tyhxzy.utils.SqlSessionUtils;
import org.apache.ibatis.session.SqlSession;

/**
 * 使用一级缓存
 */
public class TestFirstLevelCache {

    public static void main(String[] args) {
        //1.获取一个会话对象
        SqlSession session = SqlSessionUtils.getSession();
        //2.查询2次用户对象
        UserMapper userMapper = session.getMapper(UserMapper.class);
        User user = userMapper.findUserById(1);
        System.out.println("名字:" + user.getUsername());

        //3.第2次执行
        User user2 = userMapper.findUserById(1);
        System.out.println("名字:" + user2.getUsername());

        session.close();
    }

}

效果

DEBUG - ==>  Preparing: select * from user where id=? 
DEBUG - ==> Parameters: 1(Integer)
DEBUG - <==      Total: 1
名字:孙悟空
名字:孙悟空

一级缓存的分析

image-20230524172216472

案例:一级缓存的清空

清空的目的:如果进行了增删改的操作,表中的记录有可能发生变化,缓存中的数据就是脏数据。

清空的方式: 只要执行了增删改的操作,提交事务,关闭会话操作会就自动清空一级缓存的数据

需求

  1. 第一次查询以后,提交会话
  2. 再进行第二次查询,观察查询结果

代码

package com.tyhxzy.test;

import com.tyhxzy.dao.UserMapper;
import com.tyhxzy.entity.User;
import com.tyhxzy.utils.SqlSessionUtils;
import org.apache.ibatis.session.SqlSession;

/**
 * 使用一级缓存
 */
public class TestFirstLevelCache {

    public static void main(String[] args) {
        //1.获取一个会话对象
        SqlSession session = SqlSessionUtils.getSession();
        //2.查询2次用户对象
        UserMapper userMapper = session.getMapper(UserMapper.class);
        User user = userMapper.findUserById(1);
        System.out.println("名字:" + user.getUsername());
        //清除缓存
        session.clearCache();

        //3.第2次执行
        User user2 = userMapper.findUserById(1);
        System.out.println("名字:" + user2.getUsername());

        session.close();
    }

}

效果

DEBUG - ==>  Preparing: select * from user where id=? 
DEBUG - ==> Parameters: 1(Integer)
DEBUG - <==      Total: 1
名字:孙悟空

DEBUG - ==>  Preparing: select * from user where id=? 
DEBUG - ==> Parameters: 1(Integer)
DEBUG - <==      Total: 1
名字:孙悟空

小结

  1. 什么是一级缓存

    在同一个会话中起作用

  2. 如何清除一级缓存

    增删改,提交事务,关闭会话

二级缓存

目标

二级缓存的配置和使用

概述

什么是二级缓存?

范围:在多个会话中起作用,如果有2个会话进行相同查询,就会使用缓存。

步骤

  1. mybatis-config.xml开启二级缓存
<settings>
    <!-- 开启二级缓存 -->
    <setting name="cacheEnabled" value="true"/>
</settings>
  1. 缓存的实体类要序列化
为什么要序列化?把内存中对象写到硬盘的文件中
因为二级缓存缓存的数据量可能非常大,服务器不会将所有的对象都放在内存中,有些数据放在硬盘的文件中。
  
public class User implements Serializable   
  1. 创建UserMapper.xml
    1. 开启二级缓存
    2. 编写查询语句
<?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="com.tyhxzy.dao.UserMapper">
    <!--
    开启二级缓存:对当前配置文件中所有的查询进行二级缓存
    -->
    <cache/>

    <select id="findUser" resultType="com.tyhxzy.entity.User">
        select * from user where id=#{id}
    </select>

</mapper>

<cache>标签作用

  1. 所有在映射文件里的 select 语句都将被缓存。
  2. 所有在映射文件里 insert,update 和 delete 语句会清空缓存。
  3. 缓存使用“最近很少使用”算法来回收
  4. 每个缓存可以存储 1024 个列表或对象的引用。
  5. 缓存获取的对象不是共享的且对调用者是安全的,不会有其它的调用者或线程潜在修改。

案例:二级缓存

需求

通过两个 sqlSession 对象,执行两次通过id查询用户,观察发出 sql 语句的次数。

步骤

  1. 创建一个会话查询1条记录,关闭会话
  2. 再创建一个会话查询1条记录,关闭会话
  3. 观察命中的概率

代码

package com.tyhxzy.test;

import com.tyhxzy.dao.UserMapper;
import com.tyhxzy.entity.User;
import com.tyhxzy.utils.SqlSessionUtils;
import org.apache.ibatis.session.SqlSession;

/**
 * 测试二级缓存
 */
public class TestSecondCache {

    public static void main(String[] args) {
        //创建第一个会话
        SqlSession s1 = SqlSessionUtils.getSession();
        //得到代理对象
        UserMapper userMapper = s1.getMapper(UserMapper.class);
        User u1 = userMapper.findUser(1);
        System.out.println(u1);

        s1.close();

        //创建第二个会话
        SqlSession s2 = SqlSessionUtils.getSession();
        //得到代理对象
        UserMapper userMapper2 = s2.getMapper(UserMapper.class);
        User u2 = userMapper2.findUser(1);
        System.out.println(u2);

        s2.close();
    }
}

效果

DEBUG - ==>  Preparing: select * from user where id=? 
DEBUG - ==> Parameters: 1(Integer)
DEBUG - <==      Total: 1
User{id=1, username='孙悟空', birthday=1980-10-24, sex='男', address='花果山水帘洞'}
DEBUG - Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@2a556333]
DEBUG - Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@2a556333]
DEBUG - Returned connection 710239027 to pool.

命中率50%
DEBUG - Cache Hit Ratio [com.tyhxzy.dao.UserMapper]: 0.5
User{id=1, username='孙悟空', birthday=1980-10-24, sex='男', address='花果山水帘洞'}

二级缓存分析

  1. 第1个会话查询完成以后,将查询的结果放在二级缓存中
  2. 如果有其它的会话进行相同的查询,会直接在二级缓存中读取,不再访问数据库
  3. 如果有会话执行了增删改,提交事务的方法,就会清空缓存

image-20230524172255887

小结

  1. 在核心配置文件中开启二级缓存:cacheEnabled=true
  2. 对缓存实体类序列化
  3. 在配置文件中开启缓存<cache/>
posted @ 2023-05-25 08:29  YxinHaaa  阅读(60)  评论(0编辑  收藏  举报