zoukankan      html  css  js  c++  java
  • mybatis查询缓存(一、二级缓存)

    什么是查询缓存?

      缓存是介于应用程序和物理数据源之间

      mybatis提供查询缓存,用于减轻数据压力,提高数据库性能。

      mybaits提供一级缓存,和二级缓存。

      

    一级缓存是sqlSession级别的缓存。在操作数据库时需要构造sqlSession对象,在对象中有一个数据结构(HashMap),用于存储缓存数据。不同的sqlSession之间的缓存  区域(HashMap)是互不影响的。

    二级缓存是mapper级别的缓存,多个sqlSession去操作同一个Mapper的sql语句,多个SqlSession可以公用二级缓存,二级缓存是跨sqlSession的

    为什么要用缓存?

    如果缓存中有数据就不用从数据库中获取,减少了和数据之间的交互次数,大大提高系统的性能

    为什么会两种缓存方式?

      二级缓存与一级缓存区别,二级缓存的范围更大,多个sqlSession可以共享一个UserMapper的二级缓存区域。

    • 一级缓存

      • 一级缓存工作原理

        命中条件

        缓存存在一个hash表中,通过查询SQL,查询数据库,客户端协议等作为key.在判断是否命中前,MySQL不会解析SQL,而是直接使用SQL去查询缓存,SQL任何字符上的不同,如空格,注释,都会导致缓存不命中.

        如果查询中有不确定数据,例如CURRENT_DATE()和NOW()函数,那么查询完毕后则不会被缓存.所以,包含不确定数据的查询是肯定不会找到可用缓存的

        1. 服务器接收SQL,以SQL和一些其他条件为key查找缓存表(额外性能消耗)

        2. 如果找到了缓存,则直接返回缓存(性能提升)

        3. 如果没有找到缓存,则执行SQL查询,包括原来的SQL解析,优化等.

        4. 执行完SQL查询结果以后,将SQL查询结果存入缓存表(额外性能消耗)

        

     为什么sqlSession去执行commit操作(执行插入、更新、删除),清空SqlSession中的一级缓存,目的为了让缓存中存储的是最新的信息,

      避免脏读。(所谓的脏数据)

      • 一级缓存测试

        mybatis默认支持一级缓存,不需要在配置文件去配置。

      测试代码

     1 // 一级缓存测试
     2 @Test
     3 public void testCache1() throws Exception {
     4     SqlSession sqlSession = sqlSessionFactory.openSession();// 创建代理对象
     5     UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
     6 
     7     // 下边查询使用一个SqlSession
     8     // 第一次发起请求,查询id为1的用户
     9     User user1 = userMapper.getUserById(1);
    10     System.out.println(user1);
    11 
    12     // 如果sqlSession去执行commit操作(执行插入、更新、删除),清空SqlSession中的一级缓存,这样做的目的为了让缓存中存储的是最新的信息,避免脏读。
    13 
    14     // 更新user1的信息
    15     // user1.setUsername("我是测试用户");
    16     // userMapper.updateUser(user1);
    17     // //执行commit操作去清空缓存
    18     // sqlSession.commit();
    19 
    20     // 第二次发起请求,查询id为1的用户
    21     User user2 = userMapper.getUserById(1);
    22     System.out.println(user2);
    23 
    24     sqlSession.close();
    25 
    26 }

      假设我们不执行更新操作,输出:

     1 DEBUG [main] - Opening JDBC Connection
     2 DEBUG [main] - Created connection 110771485.
     3 DEBUG [main] - Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@69a3d1d]
     4 DEBUG [main] - ==>  Preparing: SELECT * FROM user WHERE id=? 
     5 DEBUG [main] - ==> Parameters: 1(Integer)
     6 DEBUG [main] - <==      Total: 1
     7 User [id=1, username=张三, sex=男]
     8 User [id=1, username=张三, sex=男]
     9 DEBUG [main] - Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@69a3d1d]
    10 DEBUG [main] - Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@69a3d1d]
    11 DEBUG [main] - Returned connection 110771485 to pool.

      使用上面更新的代码,输出:

     1 DEBUG [main] - Opening JDBC Connection
     2 DEBUG [main] - Created connection 110771485.
     3 DEBUG [main] - Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@69a3d1d]
     4 DEBUG [main] - ==>  Preparing: SELECT * FROM user WHERE id=? 
     5 DEBUG [main] - ==> Parameters: 1(Integer)
     6 DEBUG [main] - <==      Total: 1
     7 User [id=1, username=张三, sex=男]
     8 DEBUG [main] - ==>  Preparing: update user set username=?,sex=? where id=? 
     9 DEBUG [main] - ==> Parameters: 我是测试用户(String),  null, 1(Integer)
    10 DEBUG [main] - <==    Updates: 1
    11 DEBUG [main] - Committing JDBC Connection [com.mysql.jdbc.JDBC4Connection@69a3d1d]
    12 DEBUG [main] - ==>  Preparing: SELECT * FROM user WHERE id=? 
    13 DEBUG [main] - ==> Parameters: 1(Integer)
    14 DEBUG [main] - <==      Total: 1
    15 User [id=1, username= 我是测试用户, sex=男]
    16 DEBUG [main] - Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@69a3d1d]
    17 DEBUG [main] - Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@69a3d1d]
    18 DEBUG [main] - Returned connection 110771485 to pool.

      原来用户名为张三则更新为我是测试用户

      • 一级缓存应用

        正式开发,假如项目是将mybatis和spring进行整合开发....,事务控制在service中。

         一个service方法中包括 很多mapper方法调用。   

     1 service{
     2 
     3      //开始执行时,开启事务,创建SqlSession对象
     4 
     5      //第一次调用mapper的方法getUserById(1)
     6 
     7        //第二次调用mapper的方法getUserById(1),从一级缓存中取数据
     8 
     9      //方法结束,sqlSession关闭
    10 
    11     }

    如果是执行两次service调用查询相同 的用户信息,不走一级缓存,因为session方法结束,sqlSession就关闭,一级缓存就清空。

      • 缓存数据失效时机 

          在表的结构或数据发生改变时,查询缓存中的数据不再有效。有这些INSERT、UPDATE、 DELETE、TRUNCATE、ALTER TABLE、

          DROP TABLE或DROP DATABASE会导致缓存数据失效。所以查询缓存适合有大量相同查询的应用,不适合有大量数据更新的应用。

      • 可以使用下面三个SQL来清理查询缓存: 
        1、FLUSH QUERY CACHE; // 清理查询缓存内存碎片。
        2、RESET QUERY CACHE; // 从查询缓存中移出所有查询。
        3、FLUSH TABLES; //关闭所有打开的表,同时该操作将会清空查询缓存中的内容。
     
    • 二级缓存

      • 二级缓存的工作原理

          第一步:首先开启mybatis的二级缓存。

          第二步:sqlSession1去查询用户id为1的用户信息,查询到用户信息会将查询数据存储到二级缓存中。

          第三步:如果SqlSession3去执行相同 mapper下sql,执行commit提交,清空该 mapper下的二级缓存区域的数据。

          第四步:sqlSession2去查询用户id为1的用户信息,去缓存中找是否存在数据,如果存在直接从缓存中取出数据。      

          UserMapper有一个二级缓存区域(按namespace分) ,其它mapper也有自己的二级缓存区域(按namespace分)。

          每一个namespace的mapper都有一个二缓存区域,两个mapper的namespace如果相同,这两个mapper执行sql查询

          到数据将存在相同的二级缓存区域中。

     

      • 开启二级缓存

        mybaits的二级缓存是mapper范围级别,除了在SqlMapConfig.xml设置二级缓存的总开关,还要在具体的mapper.xml中开启二级缓存。

         第一步:在核心配置文件mybatis-config.xml中加入以下代码    

    1 <!-- 全局参数的配置 -->
    2     <settings>
    3         <!-- 开启二级缓存 -->
    4             <setting name="cacheEnabled" value="true"/>
    5     </settings> 

      cacheEnabled:对在此配置文件下的所有cache 进行全局性开/关设置。默认true

      

        第二步:实体类User实现序列化接口

    1 public class User implements Serializable{
    2      ......
    3        
    4 }    

      目的:为了将缓存数据取出执行反序列化操作,因为二级缓存数据存储介质多种多样,不一样在内存。

      第三步:使用Junit进行测试:

     1 // 二级缓存测试
     2     @Test
     3     public void testCache2() throws Exception {
     4         SqlSession sqlSession1 = sqlSessionFactory.openSession();
     5         SqlSession sqlSession2 = sqlSessionFactory.openSession();
     6         SqlSession sqlSession3 = sqlSessionFactory.openSession();
     7        
     8  // 创建代理对象
     9         UserMapper userMapper1 = sqlSession1.getMapper(UserMapper.class);
    10         // 第一次发起请求,查询id为1的用户
    11         User user1 = userMapper1.getUserById(1);
    12         System.out.println(user1);
    13         
    14         //这里执行关闭操作,将sqlsession中的数据写到二级缓存区域
    15         sqlSession1.close();
    16         
    17         
    18         //使用sqlSession3执行commit()操作
    19         UserMapper userMapper3 = sqlSession3.getMapper(UserMapper.class);
    20         User user  = userMapper3.getUserById(1);
    21         user.setUsername("张明明");        
           userMapper3.updateUser(user);
    22 //执行提交,清空UserMapper下边的二级缓存 23 sqlSession3.commit(); 24 sqlSession3.close(); 25 26 UserMapper userMapper2 = sqlSession2.getMapper(UserMapper.class); 27 // 第二次发起请求,查询id为1的用户 28 User user2 = userMapper2.getUserById(1); 29 System.out.println(user2); 30 31 sqlSession2.close(); 32 33 }
      • 禁用二级缓存

        在statement中设置useCache=false可以禁用当前select语句的二级缓存,即每次查询都会发出sql去查询,

        默认情况是true,即该sql使用二级缓存。

           <select id="findOrderListResultMap" resultMap="ordersUserMap" useCache="false"> 

      • 刷新缓存

        在mapper的同一个namespace中,如果有其它insert、update、delete操作数据后需要刷新缓存,如果不执行刷新缓存会出现脏读。

         设置statement配置中的flushCache="true" 属性,默认情况下为true即刷新缓存,如果改成false则不会刷新。使用缓存时如果手动

        修改数据库表中的查询数据会出现脏读。

        如下:

           <insert id="insertUser" parameterType="com.mybaits.entity.User" flushCache="true"> 

      • 二级缓存应用场景 

        对于访问多的查询请求且用户对查询结果实时性要求不高,此时可采用mybatis二级缓存技术降低数据库访问量,

        提高访问速度,业务场景比如:耗时较高的统计分析sql、电话账单查询sql等。

        实现方法如下:通过设置刷新间隔时间,由mybatis每隔一段时间自动清空缓存,根据数据变化频率设置缓存刷新间

        隔 flushInterval ,比如设置为30分钟、60分钟、24小时等,根据需求而定。

      • 二级缓存的局限性

         mybatis二级缓存对细粒度的数据级别的缓存实现不好,比如如下需求:对商品信息进行缓存,由于商品信息查询访问量大,

        但是要求用户每次都能查询最新的商品信息,此时如果使用mybatis的二级缓存就无法实现当一个商品变化时只刷新该商品的缓存

        信息而不刷新其它商品的信息,因为mybaits的二级缓存区域以mapper为单位划分,当一个商品信息变化会将所有商品信息的缓存数据

        全部清空。解决此类问题需要在业务层根据需求对数据有针对性缓存

    vue是什么?

    vue是一个当前很火的js框架。它可以将我们的数据,和显示数据的DOM文档进行绑定。一旦绑定以后,DOM和数据将会自动同步。使我们不用在考虑给DOM的某个元素去进行赋值,而是将注意力放在数据模型上。

  • 相关阅读:
    一个好的时间函数
    Codeforces 785E. Anton and Permutation
    Codeforces 785 D. Anton and School
    Codeforces 510 E. Fox And Dinner
    Codeforces 242 E. XOR on Segment
    Codeforces 629 E. Famil Door and Roads
    Codeforces 600E. Lomsat gelral(Dsu on tree学习)
    Codeforces 438D The Child and Sequence
    Codeforces 729E Subordinates
    【ATcoder】D
  • 原文地址:https://www.cnblogs.com/lhy-549/p/10087778.html
Copyright © 2011-2022 走看看