MyBatis缓存机制学习

与Hibernate一样,MyBatis 同样提供了一级缓存和二级缓存的支持。

  • 一级缓存: 基于PerpetualCache 的 HashMap本地缓存,其存储作用域为 Session,当 Session flush 或 close 之后,该Session中的所有 Cache 就将清空。
  • 二级缓存与一级缓存其机制相同,默认也是采用 PerpetualCache,HashMap存储,不同在于其存储作用域为 Mapper(Namespace),并且可自定义第三方存储源,如 Ehcache框架等。
  • 对于缓存数据更新机制,当某一个作用域(一级缓存Session/二级缓存Namespaces)的进行了 C/U/D 操作后,默认该作用域下所有 select 中的缓存将被clear。

MyBatis中一级缓存是默认开启的,即在查询中(一次SqlSession中)。只要当SqlSession不关闭,那么你的操作会默认存储使用一级缓存。

Mybatis一级缓存测试

String config = "sqlMapConfig.xml";
InputStream inputStream = Resources.getResourceAsStream(config);
SqlSessionFactory   sqlSessionFactory = new        SqlSessionFactoryBuilder().build(inputStream);
SqlSession session = sqlSessionFactory.openSession();             // 执行在bean配置文件中定义的sql语句
User user = session.selectOne("UserMapper.findById", 1);
System.out.println(user);
/*
* 一级缓存默认就会被使用
*/
  user = session.selectOne("UserMapper.findById", 1);
 System.out.println(user);
 session.close();
  /*
   1. 必须是同一个Session,如果session对象已经close()过了就不可能用了 
  */
 session = MyBatisUtil.getSqlSession();
 user = session.selectOne("UserMapper.findById", 1);
 System.out.println(user);
 /*
  2. 查询条件必须是一样的
 */
        user = session.selectOne("UserMapper.findById", 2);
        System.out.println(user);

   /*
    3. 没有执行过session.clearCache()清理缓存
    */
        //session.clearCache(); 
        user = session.selectOne("UserMapper.findById", 2);
        System.out.println(user);

      /*
       4. 没有执行过增删改的操作(这些操作都会清理缓存)
      */
        session.update("UserMapper.updateUser",
                new User(2, "user", 23));
        user = session.selectOne("UserMapper.findById", 2);
        System.out.println(user);

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39

Mybatis二级缓存测试

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

在核心配置文件SqlMapConfig.xml中加入

<settingname='cacheEnabled'value='true'/>

<!-- 全局配置参数,需要时再设置 -->

    <settings>

       <!-- 开启二级缓存 默认值为true -->

    <settingname='cacheEnabled'value='true'/>

    </settings>
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

开启二级缓存,在userMapper.xml文件中添加如下配置

<mapper namespace="me.gacl.mapping.userMapper">
<!-- 开启二级缓存这个必须开启,全部使用默认的配置-->
<cache/>
 
 
  • 1
  • 2
  • 3

具体的测试代码:

    /*
     * 测试二级缓存
     * 使用两个不同的SqlSession对象去执行相同查询条件的查询,第二次查询时不会再发送SQL语句,而是直接从缓存中取出数据
     */
        String statement = "UserMapper.findById";
        SqlSessionFactory factory = MyBatisUtil.getSqlSessionFactory();
        //开启两个不同的SqlSession
        SqlSession session1 = factory.openSession();
        SqlSession session2 = factory.openSession();
        //使用二级缓存时,User类必须实现一个Serializable接口===> User implements Serializable
        User user = session1.selectOne(statement, 1);
        session1.commit();
        /*一定要提交事务之后二级缓存才会起作用
        因为,二级缓存是从cache(mapper.xml中定义的cache)中取得
        如果session不commit,那么,数据就不会放入cache中*/
        System.out.println("user="+user);

        //由于使用的是两个不同的SqlSession对象,所以即使查询条件相同,一级缓存也不会开启使用
        user = session2.selectOne(statement, 1);
        //session2.commit();
        System.out.println("user2="+user);

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

前面在使用<cache/>时我们说全部使用默认的配置,所以仅仅写了一个标签而已,现在我们来看这个标签包含那些属性。

看如下例子,即一个常用的cache标签属性:

<cache 
eviction="FIFO"  <!--回收策略为先进先出-->
flushInterval="60000" <!--自动刷新时间60s-->
size="512" <!--最多缓存512个引用对象-->
readOnly="true"/> <!--只读-->
 
 
  • 1
  • 2
  • 3
  • 4
  • 5

eviction(回收策略)

  1. LRU – 最近最少使用的:移除最长时间不被使用的对象。(默认的属性)

  2. FIFO – 先进先出:按对象进入缓存的顺序来移除它们。

  3. SOFT – 软引用:移除基于垃圾回收器状态和软引用规则的对象。

  4. WEAK – 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。

flushInterval(刷新间隔)

可以被设置为任意的正整数,而且它们代表一个合理的毫秒形式的时间段。默认情况是不设置,也就是没有刷新间隔,缓存仅仅调用语句时刷新。

size(引用数目)

可以被设置为任意正整数,要记住你缓存的对象数目和你运行环境的可用内存资源数目。默认值是1024。

readOnly(只读)

可以被设置为true或false。只读的缓存会给所有调用者返回缓存对象的相同实例。因此这些对象不能被修改。这提供了很重要的性能优势。可读写的缓存会返回缓存对象的拷贝(通过序列化)。这会慢一些,但是安全,因此默认是false。

mybatis整合ehcache

ehcache是一个分布式缓存框架。

EhCache 是一个纯Java的进程内缓存框架,是一种广泛使用的开源Java分布式缓存,具有快速、精干等特点,是Hibernate中默认的CacheProvider。 —《百度百科》

分布缓存

我们系统为了提高系统并发,性能、一般对系统进行分布式部署(集群部署方式)

这里写图片描述

不使用分布缓存,缓存的数据在各各服务单独存储,不方便系统开发。所以要使用分布式缓存对缓存数据进行集中管理。

mybatis无法实现分布式缓存,需要和其它分布式缓存框架进行整合。

mybatis提供了一个cache接口,如果要实现自己的缓存逻辑,实现cache接口开发即可。

mybatis和ehcache整合,mybatis和ehcache整合包中提供了一个cache接口的实现类。

在项目中加入如下两个jar包:

<dependency>
    <groupId>net.sf.ehcache</groupId>
    <artifactId>ehcache</artifactId>
    <version>2.10.1</version>
</dependency>
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis-ehcache</artifactId>
    <version>1.0.0</version>
</dependency>

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

整合ehcache

配置mapper中cache中的type为ehcache对cache接口的实现类型。

<mapper namespace='UserMapper'>
<!-- 开启本mappernamespace下的二级缓存

   type:指定cache接口实现类,mybatis默认使用PerpetualCache

   要和eache整合,需要配置type为ehcahe实现cache接口的类型

 -->

<cachetype='org.mybatis.caches.ehcache.EhcacheCache'>

</cache>
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

可以根据需求调整缓存参数:

<cachetype='org.mybatis.caches.ehcache.EhcacheCache'>

        <propertyname='timeToIdleSeconds'value='3600'/>

        <propertyname='timeToLiveSeconds'value='3600'/>

        <!-- 同ehcache参数maxElementsInMemory-->

       <propertyname='maxEntriesLocalHeap'value='1000'/>

       <!-- 同ehcache参数maxElementsOnDisk -->

        <propertyname='maxEntriesLocalDisk'value='10000000'/>

        <propertyname='memoryStoreEvictionPolicy'value='LRU'/>

    </cache>
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

加入ehcache的配置文件

在classpath下配置ehcache.xml

<ehcachexmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'

    xsi:noNamespaceSchemaLocation='../config/ehcache.xsd'>

    <diskStorepath='/home/wang/cache'/>

    <defaultCache

      maxElementsInMemory='1000'

       maxElementsOnDisk='10000000'

       eternal='false'

       overflowToDisk='false'

       timeToIdleSeconds='120'

       timeToLiveSeconds='120'

       diskExpiryThreadIntervalSeconds='120'

       memoryStoreEvictionPolicy='LRU'>

    </defaultCache>

</ehcache>
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

属性说明:

  • diskStore:指定数据在磁盘中的存储位置。

  • defaultCache:当借助CacheManager.add(‘demoCache’)创建Cache时,EhCache便会采用<defalutCache/>指定的的管理策略

以下属性是必须的:

  • maxElementsInMemory - 在内存中缓存的element的最大数目

  • maxElementsOnDisk - 在磁盘上缓存的element的最大数目,若是0表示无穷大

  • eternal - 设定缓存的elements是否永远不过期。如果为true,则缓存的数据始终有效,如果为false那么还要根据timeToIdleSeconds,timeToLiveSeconds判断

  • overflowToDisk- 设定当内存缓存溢出的时候是否将过期的element缓存到磁盘上

以下属性是可选的:

  • timeToIdleSeconds - 当缓存在EhCache中的数据前后两次访问的时间超过
  • timeToIdleSeconds的属性取值时,这些数据便会删除,默认值是0,也就是可闲置时间无穷大

  • timeToLiveSeconds - 缓存element的有效生命期,默认是0.,也就是element存活时间无穷大

  • diskSpoolBufferSizeMB 这个参数设置DiskStore(磁盘缓存)的缓存区大小.默认是30MB.每个Cache都应该有自己的一个缓冲区.

  • diskPersistent在VM重启的时候是否启用磁盘保存EhCache中的数据,默认是false。

  • diskExpiryThreadIntervalSeconds - 磁盘缓存的清理线程运行间隔,默认是120秒。每个120s,相应的线程会进行一次EhCache中数据的清理工作

  • memoryStoreEvictionPolicy - 当内存缓存达到最大,有新的element加入的时候,移除缓存中element的策略。默认是LRU(最近最少使用),可选的有LFU(最不常使用)和FIFO(先进先出)

二级应用场景

对于访问多的查询请求且用户对查询结果实时性要求不高,此时可采用mybatis二级缓存技术降低数据库访问量,提高访问速度,业务场景比如:耗时较高的统计分析sql、电话账单查询sql等。

实现方法如下:通过设置刷新间隔时间,由mybatis每隔一段时间自动清空缓存,根据数据变化频率设置缓存刷新间隔flushInterval,比如设置为30分钟、60分钟、24小时等,根据需求而定。

二级缓存局限性 
mybatis二级缓存对细粒度的数据级别的缓存实现不好,对同时缓存较多条数据的缓存,比如如下需求:对商品信息进行缓存,由于商品信息查询访问量大,但是要求用户每次都能查询最新的商品信息,此时如果使用mybatis的二级缓存就无法实现当一个商品变化时只刷新该商品的缓存信息而不刷新其它商品的信息,因为mybaits的二级缓存区域以mapper为单位划分,当一个商品信息变化会将所有商品信息的缓存数据全部清空。解决此类问题需要在业务层根据需求对数据有针对性缓存

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值