MyBatis13--Second level cache in MyBatis

1. The principle of the second level cache

  As mentioned earlier, the second-level cache in mybatis is a mapper-level cache. It is worth noting that different mappers have a second-level cache, that is, the second-level caches between different mappers do not affect each other. In order to describe the L2 cache more clearly, let's first look at a schematic diagram:
L2 cache
  As can be seen from the figure:

  1. sqlSession1 queries the user information with the user id of 1, and when the user information is queried, the query data will be stored in the secondary cache of the UserMapper.
  2. If SqlSession3 executes sql under the same mapper and executes commit submission, the data in the second level cache area under the UserMapper will be cleared.
  3. sqlSession2 queries the user information whose user id is 1, goes to the cache to find out whether there is data, and if so, directly fetches the data from the cache.

  The execution principle of the cache is similar to the first-level cache mentioned above. The difference between the second-level cache and the first-level cache is that the second-level cache has a larger scope, and multiple sqlSessions can share the second-level cache area in a mapper. How does mybatis distinguish the second level cache area of ​​different mappers? It is distinguished according to the different namespaces of different mappers, that is to say, if the namespaces of two mappers are the same, even if there are two mappers, the data queried by SQL in the two mappers will also exist in the same secondary in the cache area.

2. The use of the second level cache

  After understanding the principle of the second-level cache in mybatis, the next step is how to use the second-level cache. Before using it, you must first turn on the switch of the second-level cache.

2.1 Enable L2 cache

  Since the second-level cache of mybaits is at the mapper range level, in addition to setting the general switch of the second-level cache in SqlMapConfig.xml, the second-level cache must be enabled in the specific mapper.xml. The settings are as follows:
Enable L2 cache
  This is set in SqlMapConfig.xml, and it has to be set in the specific mapper.xml, as follows: As
L2 cache in mapper
  you can see, there is only one <cache>label in the specific mapper, and nothing is configured. This is because in mybatis There is a default implementation. If we do not configure it, then the default implementation will be used by default. There is a cache interface and this default implementation in the core package of mybatis. I took a screenshot:
Second-level cache default implementation class
  So I understand why it can be used without configuration. There is only this default implementation class in mybatis. If you do not use mybatis If the second-level cache is the default, you need to implement the cache interface yourself, and then configure it in mapper.xml. I will talk about this below, and now use the second-level cache first!

2.2 Implement the Serializable interface for the po class

  After enabling the second-level cache, you also need to implement the Serializable interface for the pojo to be cached. In order to take out the cached data and perform the deserialization operation, because the second-level cache data storage medium is various, it may not only exist in the memory, but may exist in the hard disk. If we want to fetch this cache again, we need to deserialize it. Therefore, it is recommended that all pojos in mybatis implement the Serializable interface. Here's a screenshot of User as an example:
Serialization

2.3 Test the second level cache of mybatis

@Test
public void testCache2() throws Exception {
    SqlSession sqlSession1 = sqlSessionFactory.openSession();
    SqlSession sqlSession2 = sqlSessionFactory.openSession();
    SqlSession sqlSession3 = sqlSessionFactory.openSession();
    // 创建代理对象
    UserMapper userMapper1 = sqlSession1.getMapper(UserMapper.class);
    // 第一次发起请求,查询id为1的用户
    User user1 = userMapper1.findUserById(1);
    System.out.println(user1);  
    //这里执行关闭操作,将sqlsession中的数据写到二级缓存区域
    sqlSession1.close();

    //sqlSession3用来清空缓存的,如果要测试二级缓存,需要把该部分注释掉
    //使用sqlSession3执行commit()操作
    UserMapper userMapper3 = sqlSession3.getMapper(UserMapper.class);
    User user  = userMapper3.findUserById(1);
    user.setUsername("倪升武");
    userMapper3.updateUser(user);
    //执行提交,清空UserMapper下边的二级缓存
    sqlSession3.commit();
    sqlSession3.close();

    UserMapper userMapper2 = sqlSession2.getMapper(UserMapper.class);
    // 第二次发起请求,查询id为1的用户
    User user2 = userMapper2.findUserById(1);
    System.out.println(user2);

    sqlSession2.close();

}
    
    

      Let's comment out the sqlSession3 part to test the result of the second-level cache:
    L2 cache
      when we add the sqlSession3 part, then test the second-level cache result:
    L2 cache 2
      here, we understand the execution principle of the second-level cache in mybatis, this Similar to hibernate. You can also look at the blog post I wrote about the second-level cache of hibernate (but it is recommended to read the ehcache section below and then look at hibernate).

    2.4 Other configurations (useCache and flushCache)

      Configuration items such as userCache and flushCache can also be configured in mybatis. userCache is used to set whether to disable the second-level cache . Setting useCache=false in the statement can disable the second-level cache of the current select statement, that is, every query will issue sql to query , the default is true, that is, the sql uses the second level cache.

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

      In this case, the latest data sql is required for each query. It is necessary to set useCache=false, disable the second-level cache, and obtain it directly from the database.
    In the same namespace of mapper, if there are other insert, update, delete operation data, the cache needs to be refreshed. If the cache refresh is not performed, dirty reads will occur. Set the flushCache=”true” property in the statement configuration. By default, it is true, that is, the cache is refreshed. If it is changed to false, it will not be refreshed. Dirty reads will occur if the query data in the database table is manually modified when using the cache.

    <insert id="insertUser" parameterType="cn.itcast.mybatis.po.User" flushCache="true">
        
        
    • 1

      Generally, the cache needs to be refreshed after the commit operation is performed. flushCache=true means that the cache is refreshed, which can avoid the dirty reading of the database. So we don't need to set it, the default is fine, just mention it here.

    3. MyBatis integrates ehcache distributed caching framework

    3.1 The origin of the problem

      The above part mainly summarizes the use of the second-level cache in mybatis, but the default second-level cache in mybatis has a drawback, that is, the distributed cache cannot be implemented. What does it mean? That is to say, the cached data is on its own server. Suppose there are two servers A and B. When the user accesses the A server, the cache after the query will be placed on the A server. Suppose now that a user is accessing the Server B, then he can't get the cache just now on server B, as shown in the following figure:
    Cache drawbacks
      So in order to solve this problem, we have to find a distributed cache, which is specially used to store cached data, so that different servers need to The cached data is stored there, and the cached data is also taken from it, as shown in the following figure:
    Distributed cache
      This can solve the problems mentioned above. In order to improve the concurrent performance of the system, we generally carry out the above distributed deployment of the system ( Cluster deployment), so use distributed cache to centrally manage cached data. However, mybatis cannot implement distributed caching and needs to be integrated with other distributed caching frameworks. Here we mainly introduce ehcache.

    3.2 Integration methods

      上文一开始提到过,mybatis提供了一个cache接口,如果要实现自己的缓存逻辑,实现cache接口开发即可。mybatis本身默认实现了一个,但是这个缓存的实现无法实现分布式缓存,所以我们要自己来实现。ehcache分布式缓存就可以,mybatis提供了一个针对cache接口的ehcache实现类,这个类在mybatis和ehcache的整合包中。所以首先我们需要导入整合包(点我下载)。
    jar包
      导入了jar包后,配置mapper中cache中的type为ehcache对cache接口的实现类型。ehcache对cache接口有一个实现类为:
    Implementation class
      我们将该类的完全限定名写到type属性中即可,如下:
    configure
      OK,配置完成,现在mybatis就会自动去执行这个ehcache实现类了,就不会使用自己默认的二级缓存了,但是使用ehcache还有一个缓存配置别忘了,在classpath下新建一个ehcache.xml文件:

    <ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
        xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">
    
        <diskStore path="F:\develop\ehcache"/>
    
        <defaultCache
                maxElementsInMemory="10000"
                eternal="false"
                timeToIdleSeconds="120"
                timeToLiveSeconds="120"
                maxElementsOnDisk="10000000"
                diskExpiryThreadIntervalSeconds="120"
                memoryStoreEvictionPolicy="LRU">
            <persistence strategy="localTempSwap"/>
        </defaultCache>
    </ehcache>
        
        

        这里面配置的作用跟hibernate差不多,大家可以去参考我那篇hibernate二级缓存的博文。接下来就是测试了,还是用上面的那个测试程序,因为只改掉了缓存,其他没动。到此为止,mybatis的二级缓存差不多就总结完了。

      4. 二级缓存的应用场景和局限性

        对于访问多的查询请求且用户对查询结果实时性要求不高,此时可采用mybatis二级缓存技术降低数据库访问量,提高访问速度,业务场景比如:耗时较高的统计分析sql、电话账单查询sql等。实现方法如下:通过设置刷新间隔时间,由mybatis每隔一段时间自动清空缓存,根据数据变化频率设置缓存刷新间隔flushInterval,比如设置为30分钟、60分钟、24小时等,根据需求而定。
        mybatis二级缓存对细粒度的数据级别的缓存实现不好,比如如下需求:对商品信息进行缓存,由于商品信息查询访问量大,但是要求用户每次都能查询最新的商品信息,此时如果使用mybatis的二级缓存就无法实现当一个商品变化时只刷新该商品的缓存信息而不刷新其它商品的信息,因为mybaits的二级缓存区域以mapper为单位划分的,当一个商品信息变化会将所有商品信息的缓存数据全部清空。解决此类问题可能需要在业务层根据需求对数据有针对性缓存。
        


      1. 二级缓存的原理

        前面介绍了,mybatis中的二级缓存是mapper级别的缓存,值得注意的是,不同的mapper都有一个二级缓存,也就是说,不同的mapper之间的二级缓存是互不影响的。为了更加清楚的描述二级缓存,先来看一个示意图:
      L2 cache
        从图中可以看出:

      1. sqlSession1去查询用户id为1的用户信息,查询到用户信息会将查询数据存储到该UserMapper的二级缓存中。
      2. 如果SqlSession3去执行相同 mapper下sql,执行commit提交,则会清空该UserMapper下二级缓存区域的数据。
      3. sqlSession2去查询用户id为1的用户信息,去缓存中找是否存在数据,如果存在直接从缓存中取出数据。

        缓存的执行原理和前面提到的一级缓存是差不多的,二级缓存与一级缓存区别在于二级缓存的范围更大,多个sqlSession可以共享一个mapper中的二级缓存区域。mybatis是如何区分不同mapper的二级缓存区域呢?它是按照不同mapper有不同的namespace来区分的,也就是说,如果两个mapper的namespace相同,即使是两个mapper,那么这两个mapper中执行sql查询到的数据也将存在相同的二级缓存区域中。

      2. 二级缓存的使用

        明白了mybatis中二级缓存的原理后,接下来就是如何使用二级缓存了。在使用之前,首先得开启二级缓存的开关。

      2.1 开启二级缓存

        由于mybaits的二级缓存是mapper范围级别,所以除了在SqlMapConfig.xml设置二级缓存的总开关外,还要在具体的mapper.xml中开启二级缓存。设置如下:
      Enable L2 cache
        这是在SqlMapConfig.xml中设置的,还得在具体的mapper.xml中设置,如下:
      L2 cache in mapper
        可以看到,具体的mapper中仅仅就一个<cache>标签,并没有配置啥东西,这是因为mybatis中有默认的实现,我们如果不配置,那么就默认使用那个默认的实现。在mybatis的核心包里有cache的接口和这个默认的实现,我截个图:
      Second-level cache default implementation class
        所以就明白了,为啥不用配置都可以使用,mybatis中也就只有这一个默认实现类,如果不使用mybatis的默认二级缓存的话,就需要自己实现cache接口,然后再在mapper.xml中配置一下了,关于这个我在下面再谈,现在先把二级缓存用起来!

      2.2 将po类实现Serializable接口

        开启了二级缓存后,还需要将要缓存的pojo实现Serializable接口,为了将缓存数据取出执行反序列化操作,因为二级缓存数据存储介质多种多样,不一定只存在内存中,有可能存在硬盘中,如果我们要再取这个缓存的话,就需要反序列化了。所以建议mybatis中的pojo都去实现Serializable接口。下面以User为例截个图:
      Serialization

      2.3 测试mybatis的二级缓存

      @Test
      public void testCache2() throws Exception {
          SqlSession sqlSession1 = sqlSessionFactory.openSession();
          SqlSession sqlSession2 = sqlSessionFactory.openSession();
          SqlSession sqlSession3 = sqlSessionFactory.openSession();
          // 创建代理对象
          UserMapper userMapper1 = sqlSession1.getMapper(UserMapper.class);
          // 第一次发起请求,查询id为1的用户
          User user1 = userMapper1.findUserById(1);
          System.out.println(user1);  
          //这里执行关闭操作,将sqlsession中的数据写到二级缓存区域
          sqlSession1.close();
      
          //sqlSession3用来清空缓存的,如果要测试二级缓存,需要把该部分注释掉
          //使用sqlSession3执行commit()操作
          UserMapper userMapper3 = sqlSession3.getMapper(UserMapper.class);
          User user  = userMapper3.findUserById(1);
          user.setUsername("倪升武");
          userMapper3.updateUser(user);
          //执行提交,清空UserMapper下边的二级缓存
          sqlSession3.commit();
          sqlSession3.close();
      
          UserMapper userMapper2 = sqlSession2.getMapper(UserMapper.class);
          // 第二次发起请求,查询id为1的用户
          User user2 = userMapper2.findUserById(1);
          System.out.println(user2);
      
          sqlSession2.close();
      
      }
        
        

          我们先把sqlSession3部分注释掉来测试一下二级缓存的结果:
        L2 cache
          当我们把sqlSession3部分加上后,再测试一下二级缓存结果:
        L2 cache 2
          到这里,就明白了mybatis中二级缓存的执行原理了,这个跟hibernate还是有点像的。也可以对照着我写的hibernate的二级缓存的博文看(不过建议把下面的ehcache部分看完再对照hibernate看)。

        2.4 其他配置(useCache和flushCache)

          mybatis中还可以配置userCache和flushCache等配置项,userCache是用来设置是否禁用二级缓存的,在statement中设置useCache=false可以禁用当前select语句的二级缓存,即每次查询都会发出sql去查询,默认情况是true,即该sql使用二级缓存。

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

          这种情况是针对每次查询都需要最新的数据sql,要设置成useCache=false,禁用二级缓存,直接从数据库中获取。
        在mapper的同一个namespace中,如果有其它insert、update、delete操作数据后需要刷新缓存,如果不执行刷新缓存会出现脏读。 设置statement配置中的flushCache=”true” 属性,默认情况下为true,即刷新缓存,如果改成false则不会刷新。使用缓存时如果手动修改数据库表中的查询数据会出现脏读。

        <insert id="insertUser" parameterType="cn.itcast.mybatis.po.User" flushCache="true">
          
          
        • 1

          一般下执行完commit操作都需要刷新缓存,flushCache=true表示刷新缓存,这样可以避免数据库脏读。所以我们不用设置,默认即可,这里只是提一下。

        3. MyBatis整合ehcache分布式缓存框架

        3.1 问题的由来

          上面的部分主要总结了一下mybatis中二级缓存的使用,但是mybatis中默认自带的二级缓存有个弊端,即无法实现分布式缓存,什么意思呢?就是说缓存的数据在自己的服务器上,假设现在有两个服务器A和B,用户访问的时候访问了A服务器,查询后的缓存就会放在A服务器上,假设现在有个用户访问的是B服务器,那么他在B服务器上就无法获取刚刚那个缓存,如下图所示:
        Cache drawbacks
          所以我们为了解决这个问题,就得找一个分布式的缓存,专门用来存储缓存数据的,这样不同的服务器要缓存数据都往它那里存,取缓存数据也从它那里取,如下图所示:
        Distributed cache
          这样就能解决上面所说的问题,为了提高系统并发性能、我们一般对系统进行上面这种分布式部署(集群部署方式),所以要使用分布式缓存对缓存数据进行集中管理。但是mybatis无法实现分布式缓存,需要和其它分布式缓存框架进行整合,这里主要介绍ehcache。

        3.2 整合方法

          上文一开始提到过,mybatis提供了一个cache接口,如果要实现自己的缓存逻辑,实现cache接口开发即可。mybatis本身默认实现了一个,但是这个缓存的实现无法实现分布式缓存,所以我们要自己来实现。ehcache分布式缓存就可以,mybatis提供了一个针对cache接口的ehcache实现类,这个类在mybatis和ehcache的整合包中。所以首先我们需要导入整合包(点我下载)。
        jar包
          导入了jar包后,配置mapper中cache中的type为ehcache对cache接口的实现类型。ehcache对cache接口有一个实现类为:
        Implementation class
          我们将该类的完全限定名写到type属性中即可,如下:
        configure
          OK,配置完成,现在mybatis就会自动去执行这个ehcache实现类了,就不会使用自己默认的二级缓存了,但是使用ehcache还有一个缓存配置别忘了,在classpath下新建一个ehcache.xml文件:

        <ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
            xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">
        
            <diskStore path="F:\develop\ehcache"/>
        
            <defaultCache
                    maxElementsInMemory="10000"
                    eternal="false"
                    timeToIdleSeconds="120"
                    timeToLiveSeconds="120"
                    maxElementsOnDisk="10000000"
                    diskExpiryThreadIntervalSeconds="120"
                    memoryStoreEvictionPolicy="LRU">
                <persistence strategy="localTempSwap"/>
            </defaultCache>
        </ehcache>
          
          

            这里面配置的作用跟hibernate差不多,大家可以去参考我那篇hibernate二级缓存的博文。接下来就是测试了,还是用上面的那个测试程序,因为只改掉了缓存,其他没动。到此为止,mybatis的二级缓存差不多就总结完了。

          4. 二级缓存的应用场景和局限性

            For query requests with many accesses and users do not have high requirements for real-time query results, mybatis secondary cache technology can be used to reduce database access and improve access speed. Business scenarios such as sql, telephone bills, and time-consuming statistical analysis Query sql, etc. The implementation method is as follows: by setting the refresh interval, mybatis will automatically clear the cache at regular intervals, and set the cache refresh interval flushInterval according to the data change frequency, such as 30 minutes, 60 minutes, 24 hours, etc., depending on the demand.
            The mybatis secondary cache is not good for fine-grained data-level caching, such as the following requirements: Cache commodity information, due to the large number of visits to commodity information queries, but requires users to query the latest commodity information every time. At this time, if Using the second-level cache of mybatis cannot realize that when a product changes, only the cache information of the product is refreshed without refreshing the information of other products, because the second-level cache area of ​​mybaits is divided by mapper. When a product information changes, it will be All cached data of product information are cleared. Solving such problems may require targeted caching of data according to needs at the business layer.
            


          Guess you like

          Origin http://10.200.1.11:23101/article/api/json?id=326946143&siteId=291194637