13.缓存

简介

1.什么是缓存
    1.存在内存中的临时数据
    2.将用户经常查询的数据放在缓存(内存)中,用户去查询数据就不用从磁盘上(关系型数据库数据文件)查询,
    从缓存中查询,从而提高查询效率,解决高并发系统性能问题
2.为什么使用缓存
    减少和数据库的交互次数,减少系统开销,提高系统效率
3.什么样的数据使用缓存
    经常查询并且不经常改变的数据
    
mybatis缓存
    1.mybatis包含一个非常强大的查询缓存特性,它可以非常方便的定制和配置缓存,缓存可以极大的提升查询效率
    2.mybatis系统中默认定义了两级缓存:一级缓存和二级缓存
        2.1默认情况下,只有一级缓存开启。(SqlSession级别的缓存,也称为本地缓存)
        2.2二级缓存需要手动开启和配置,他是基于namespace级别的缓存
        2.3为了提高扩展性,mybatis定义了缓存接口Cache,我们可以通过实现Cache接口来定义二级缓存

一级缓存

一级缓存也叫本地缓存
    1.与数据库同义词会话启脚查询到的数据会放到本地缓存中
    2.以后想要获取相同的数据,直接从缓存中拿,没有必要去查询数据库

样例1:在同一个sqlsession中查询同一id用户
    @org.junit.Test
    public void testCatch(){
         SqlSession sqlSession = MybatisTools.getSqlSession();
         UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        User user1 = mapper.queryUserById(1);
        System.out.println("1:"+user1);
        User user2 = mapper.queryUserById(1);
        System.out.println("2"+user2);
        System.out.println(user1==user2);
        sqlSession.close();
    }
    控制台输出:
        Checking to see if class cn.com.wmd.dao.PetMapper matches criteria [is assignable to Object]
        Checking to see if class cn.com.wmd.dao.UserMapper matches criteria [is assignable to Object]
        Opening JDBC Connection
        Created connection 1240232440.
        --------------------------------------------------------->>发现sql语句只执行了一遍
        ==>  Preparing: select * from public.user where id=?
        ==> Parameters: 1(Integer)
        <==    Columns: id, name, pwd
        <==        Row: 1, 无法, 五天
        <==      Total: 1
        1:User(id=1, name=无法, pwd=五天)
        2:User(id=1, name=无法, pwd=五天)
        true----------------------------------------------------->>两次查询出的对象完全一致!
    结论:
        在同一sqlsession中多次执行的同一sql语句会放入一级缓存中,下次会去缓存中获取

样例2:不同的sqlsession执行同一sql语句
        @org.junit.Test
    public void testCatch(){
        SqlSession sqlSession1 = MybatisTools.getSqlSession();
        UserMapper mapper1 = sqlSession1.getMapper(UserMapper.class);
        User user1 = mapper1.queryUserById(1);
        System.out.println("1:"+user1);
        sqlSession1.close();
        System.out.println("----------------------分割线---------------------");
        重点:两个sqlsession执行同一sql语句
        SqlSession sqlSession2 = MybatisTools.getSqlSession();
        UserMapper mapper2 = sqlSession2.getMapper(UserMapper.class);
        User user2= mapper2.queryUserById(1);
        System.out.println("2"+user2);
        sqlSession2.close();
        System.out.println(user1==user2);
    }
    控制台输出:
        Checking to see if class cn.com.wmd.dao.PetMapper matches criteria [is assignable to Object]
        Checking to see if class cn.com.wmd.dao.UserMapper matches criteria [is assignable to Object]
        Opening JDBC Connection
        Created connection 1240232440.
        ==>  Preparing: select * from public.user where id=?
        ==> Parameters: 1(Integer)
        <==    Columns: id, name, pwd
        <==        Row: 1, 无法, 五天
        <==      Total: 1
        1:User(id=1, name=无法, pwd=五天)
        Closing JDBC Connection [org.postgresql.jdbc.PgConnection@49ec71f8]
        Returned connection 1240232440 to pool.
        ----------------------分割线---------------------
        Opening JDBC Connection
        Checked out connection 1240232440 from pool.
        ==>  Preparing: select * from public.user where id=?
        ==> Parameters: 1(Integer)
        <==    Columns: id, name, pwd
        <==        Row: 1, 无法, 五天
        <==      Total: 1
        2User(id=1, name=无法, pwd=五天)
        Closing JDBC Connection [org.postgresql.jdbc.PgConnection@49ec71f8]
        Returned connection 1240232440 to pool.
        false
    结论:发现同一查询语句执行了两次,并且两次查询出的对象不是同一对象
样例3:同一sqlsession执行同一sql语句,但在期间执行更新操作!
    @org.junit.Test
    public void testCatch(){
        SqlSession sqlSession = MybatisTools.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        User user1 = mapper.queryUserById(1);
        System.out.println("1:"+user1);
        Map<String, Object> map=new HashMap<String, Object>();
        map.put("id",1);
        map.put("pwd","wmd");
        重点:在两次执行sql中执行更新操作!
        mapper.updatePwd(map);
        User user2 = mapper.queryUserById(1);
        System.out.println("2:"+user2);
        System.out.println(user1==user2);
        sqlSession.close();
    }
    输出:发现执行了三次sql:两次相同查询,一次更新,并且两次查询出的对象不同
        ==>  Preparing: select * from public.user where id=?
        ==> Parameters: 1(Integer)
        <==    Columns: id, name, pwd
        <==        Row: 1, 无法, 五天
        <==      Total: 1
        1:User(id=1, name=无法, pwd=五天)
        ==>  Preparing: update public."user" SET pwd=? WHERE id=?
        ==> Parameters: wmd(String), 1(Integer)
        <==    Updates: 1
        ==>  Preparing: select * from public.user where id=?
        ==> Parameters: 1(Integer)
        <==    Columns: id, name, pwd
        <==        Row: 1, 无法, wmd
        <==      Total: 1
        2:User(id=1, name=无法, pwd=wmd)
        false


结论:一级缓存默认存在,并且在同一sqlsession中有效,若该sqlsession关闭,下次执行会重新查询
更新/插入/删除操作会清空缓存,下次重新查询!

二级缓存

1.二级缓存也叫全局缓存,一级缓存作用域太低了,所以诞生了二级缓存
2.基于namespace级别的缓存,一个名称空间(即一个dao层接口),对应一个二级缓存
3.工作机制:
    1.一个会话查询一条数据,这个数据就会被放到当前会话的一级缓存中
    2.如果当前缓存关闭了,这个会话对应的一级缓存就会销毁;但是我们想要会话关闭了,一级缓存中的数据会被保存到二级缓存中
    3.新的会话查询信息,就可以从二级缓存中获取内容
    4.不同的mapper查询出的数据会被放到自己对应的缓存中
    
    
样例:
    1.在mybaits的主配置文件中配置设置:
        <settings>
            <!--配置日志打开--->
            <setting name="logImpl" value="STDOUT_LOGGING"/>
            重点:配置全局缓存设置,虽然默认是true,但是还是显示配置下!
            <!--设置全局缓存开关-->
            <setting name="cacheEnabled" value="true"/>
        </settings>
    2.在mapper.xml中配置<cache/>标签,也可以自定义一些参数,参数内容意思参考:https://mybatis.org/mybatis-3/zh/sqlmap-xml.html#cache
        1.可以直接配置个:<cache/>
        2.也可以配置各种参数:参数内容意思参考:https://mybatis.org/mybatis-3/zh/sqlmap-xml.html#cache
            <cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/>
    3.mapper.xml中的sql语句
        <select id="queryUserByIdByBq" resultType="user">
            select * from public."user" where id=#{id}
        </select>
    4.测试
        @org.junit.Test
        public void testCatch2(){
            SqlSession sqlSession1 = MybatisTools.getSqlSession();
            UserMapper mapper1 = sqlSession1.getMapper(UserMapper.class);
            User user1 = mapper1.queryUserByIdByBq(1);
            System.out.println("1:"+user1);
            sqlSession1.close();
            重点:虽然此处关闭了sqlsesson,但是这两个sql只执行一次,并且查询出的对象相同
            System.out.println("---------------------分割线---------------------");
            SqlSession sqlSession2 = MybatisTools.getSqlSession();
            UserMapper mapper2 = sqlSession2.getMapper(UserMapper.class);
            User user2 = mapper2.queryUserByIdByBq(1);
            System.out.println("2"+user2);
            System.out.println(user1==user2);
            sqlSession2.close();
        }
注意点:
    但如果是标签:
        @Select("select * from public.user where id=#{id}")
        User queryUserById(@Param("id") int  id);
    这种形式的,即使开了全局缓存,两次不同的sqlsession查询也会查询查询两次,貌似全局缓存对sql标签标注的方法不起作用!

缓存原理

缓存的执行顺序
当查询语句过来时
    1.先看二级缓存中是否有该记录
    2.再看一级缓存中是否有该记录
    3.如果都没有则查询数据库

自定义缓存

...

posted @ 2022-05-13 20:04  努力的达子  阅读(347)  评论(0编辑  收藏  举报