您的位置:首页 > 其它

【MyBatis学习13】一、二级缓存及分布式实现

2017-03-21 19:33 435 查看
本文博客地址:http://blog.csdn.net/soonfly/article/details/64482584 (转载请注明出处)

MyBatis将数据缓存设计成两级结构,分为一级缓存、二级缓存。一级、二级只是命名而已,实际上并不是递进关系。

一、一级缓存

MyBatis在SqlSession对象中建立一个简单的缓存,将每次查询到的结果结果缓存起来,当下次查询的时候,如果判断先前有完全一样的查询,会直接从缓存中直接将结果取出,返回给用户。其内部就是通过一个简单的
HashMap<k,v>
来实现的。

1、作用范围

每当我们使用MyBatis开启一次和数据库的会话,MyBatis会创建出一个SqlSession对象表示一次数据库会话。一级缓存是Session会话级别的缓存,位于数据库会话的SqlSession对象之中,又被称之为本地缓存。一级缓存是MyBatis内部特性,用户不能配置。且默认情况下mybatis自动支持这种缓存,用户没有定制它的权利(除非通过开发插件对它进行修改)。

每个SqlSession中的缓存都是独立的。



2、生命周期

(1) 如果SqlSession调用了close()方法,会释放掉一级缓存PerpetualCache对象,一级缓存将不可用;

(2) 如果SqlSession调用了clearCache(),会清空PerpetualCache对象中的数据,但是该对象仍可使用;

(3) SqlSession中执行了任何一个update操作(update()、delete()、insert()) ,都会清空PerpetualCache对象的数据,但是该对象可以继续使用;



二、二级缓存

1、与一级缓存相比,二级缓存的范围更大。多个sqlSession可以共享一个UserMapper的二级缓存区域。其内部也是通过
HashMap<k,v>
来实现的。

2、每个Mapper对象(如:Mapper.xml)都会有一个与之对应的二级缓存区域。

3、mybatis根据Mapper对象的namespace区分,如果两个mapper的namespace相同,即使是两个mapper,那么这两个mapper中执行sql查询到的数据也将存在相同的二级缓存区域中。可以认为在hashMap中存储时,是以namespace作为主键的。

4、如果两个mapper的namespace如果相同,任意一个mapper的写操作,都会清空整个namespace缓存。



mybaits的二级缓存是mapper范围级别,要想使某条Select查询支持二级缓存,你需要做以下几步开启:

1. 设置 MyBatis支持二级缓存的总开关:全局配置变量参数
cacheEnabled="true"


<settings>
<!-- 对在此配置文件下的所有cache 进行全局性开/关设置  默认值为true -->
<setting name="cacheEnabled" value="true"/>
</settings>


2. 该select语句所在的Mapper,配置了 或节点,并且有效

<!-- 在UserMapper.xml中开启二缓存,UserMapper.xml下的任意sql执行完成后,都会存储到它的缓存区域(HashMap) -->
<mapper namespace="twm.mybatisdemo.mapper.UserMapper">
<!-- 开启本mapper namespace下的二级缓存 -->
<cache></cache>


可以看到
<cache>
没有作配置,即采用了默认设置。

Mybatis Cache参数设置:

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

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

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

eviction(回收策略)。可用的回收策略有:

1.LRU – 最近最少使用的:移除最长时间不被使用的对象。

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

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

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

默认的是:LRU

如下例子:

<cache  eviction="FIFO" flushInterval="60000"  size="512" readOnly="true"/>


这个更高级的配置创建了一个 FIFO 缓存,并每隔 60 秒刷新,存数结果对象或列表的 512 个引用,而且返回的对象被认为是只读的,因此在不同线程中的调用者之间修改它们会导致冲突。

3. 需要用到缓存的select语句的参数
useCache="true"


<select id="selectById" parameterType="int" resultType="User" useCache="true">
select * from user where id=#{id}
</select>


4、POJO类序列化

pojo类实现序列化接口是为了将缓存数据取出执行反序列化操作。因为二级缓存数据存储介质多种多样,不一定都是存在内存,有可能是硬盘或者远程服务器。

public class User implements Serializable{
private static final long serialVersionUID = 1L;
Integer id;
....
}


禁用二级缓存:

在statement中设置useCache=false可以禁用当前select语句的二级缓存,即每次查询都会发出sql去查询,默认情况是true,即该sql使用二级缓存。
<selectid="findOrderListResultMap" resultMap="ordersUserMap" useCache="false">


总结:针对每次查询都需要最新的数据sql,要设置成
useCache=false
,禁用二级缓存。

三、二级缓存分布式实现

mybatis中默认自带的二级缓存实现(
PerpetualCache
)是无法做到分布式的。

Java提供了一个开源的分布式缓存EhCache。它是一个纯Java的开源分布式缓存(进程内缓存),具有快速、精干等特点,也是hibernate中默认的CacheProvider。

1、分布式缓存

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



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

2、mybatis和EhCache整合

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

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

2.1 加入ehcache包(红框中的两个包)。



2.2 整合ehcache

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

<mapper namespace="twm.mybatisdemo.mapper.UserMapper">
<!-- 开启本mapper namespace下的二级缓存
type:指定cache接口实现类,mybatis默认使用PerpetualCache
要和eache整合,需要配置type为ehcahe实现cache接口的类型
-->
<cache type="org.mybatis.caches.ehcache.EhcacheCache">
</cache>


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

<cache type="org.mybatis.caches.ehcache.EhcacheCache">
<property name="timeToIdleSeconds" value="3600"/>
<property name="timeToLiveSeconds" value="3600"/>
<!-- 同ehcache参数maxElementsInMemory-->
<property name="maxEntriesLocalHeap"value="1000"/>
<!-- 同ehcache参数maxElementsOnDisk -->
<property name="maxEntriesLocalDisk" value="10000000"/>
<property name="memoryStoreEvictionPolicy" value="LRU"/>
</cache>


2.3 加入ehcache的配置文件

在classpath下配置ehcache.xml

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


属性说明:

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(先进先出)

3、二级应用场景

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

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

4、二级缓存局限性

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

本文博客地址:http://blog.csdn.net/soonfly/article/details/64482584 (转载请注明出处)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: