读书人

二级缓存根本说明

发布时间: 2012-08-08 14:32:45 作者: rapoo

二级缓存基本说明
一、二级缓存基本说明:

1、ehcache.xml配置说明,开发中放在src下

<diskStore>元素:指定一个文件目录,当指定的内存不够时,把数据写到硬盘上时,将把数据写到这个文件目录下。 下面的参数这样解释:

user.home 用户主目录

user.dir 用户当前工作目录

java.io.tmpdir 默认临时文件路径

<defaultCache>元素:设定缓存的默认数据过期策略,如果没有任何设置,将使用该策略。

<cache>元素:设定具体的命名缓存的数据过期策略。



<cache>元素的属性

name:缓存名称。通常为缓存对象的类名(非严格标准),如果为实体对象的包名称.类名称时,那么实体的配置中可以省去<cache标签 的 region="regionName" 属性的配置。

maxElementsInMemory:设置基于内存的缓存可存放对象的最大数目。

maxElementsOnDisk:设置基于硬盘的缓存可存放对象的最大数目。

eternal:如果为true,表示对象永远不会过期,此时会忽略timeToIdleSeconds和timeToLiveSeconds属性,默认为false;

timeToIdleSeconds: 设定允许对象处于空闲状态的最长时间,以秒为单位。当对象自从最近一次被访问后,如果处于空闲状态的时间超过了timeToIdleSeconds属性值,这个对象就会过期。当对象过期,EHCache将把它从缓存中清空。只有当eternal属性为false,该属性才有效。如果该属性值为0,则表示对象可以无限期地处于空闲状态。

timeToLiveSeconds:设定对象允许存在于缓存中的最长时间,以秒为单位。当对象自从被存放到缓存中后,如果处于缓存中的时间超过了 timeToLiveSeconds属性值,这个对象就会过期。当对象过期,EHCache将把它从缓存中清除。只有当eternal属性为false,该属性才有效。如果该属性值为0,则表示对象可以无限期地存在于缓存中。timeToLiveSeconds必须大于timeToIdleSeconds属性,才有意义。

overflowToDisk:如果为true,表示当基于内存的缓存中的对象数目达到了maxElementsInMemory界限后,会把益出的对象写到基于硬盘的缓存中。注意:如果缓存的对象要写入到硬盘中的话,则该对象必须实现了Serializable接口才行。

diskPersistent:是否在磁盘上持久化。指重启jvm后,数据是否有效。默认为false。

diskExpiryThreadIntervalSeconds:对象检测线程运行时间间隔。标识对象状态的线程多长时间运行一次。

diskSpoolBufferSizeMB:DiskStore使用的磁盘大小,默认值30MB。每个cache使用各自的DiskStore。

memoryStoreEvictionPolicy:缓存对象清除策略。有三种:

a、 FIFO ,first in first out ,这个是大家最熟的,先进先出,不多讲了

b、 LFU , Less Frequently Used ,就是上面例子中使用的策略,直白一点就是讲一直以来最少被使用的。如上面所讲,缓存的元素有一个hit 属性,hit 值最小的将会被清出缓存。

c、 LRU ,Least Recently Used ,最近最少使用的,缓存的元素有一个时间戳,当缓存容量满了,而又需要腾出地方来缓存新的元素的时候,那么现有缓存元素中时间戳离当前时间最远的元素将被清出缓存。

针对查询缓存专用的标签(其属性与上面用法一致):
开启查询缓存后会有两个缓存区:
1、一个用于保存查询结果集 (org.hibernate.cache.StandardQueryCache)(查询缓存默认使用这个作为规则,也可以单独定制);
<cache name="org.hibernate.cache.StandardQueryCache"
maxElementsInMemory="50" eternal="false" timeToIdleSeconds="10"
timeToLiveSeconds="10" overflowToDisk="false" />
2、另一个则用于保存最近查询的一系列表的时间戳(org.hibernate.cache.UpdateTimestampsCache)。
请注意:在查询缓存中,它并不缓存结果集中所包含的实体的确切状态;它只缓存这些实体的标识符属性的值、以及各值类型的结果。
需要将打印sql语句与最近的cache内容相比较,将不同之处修改到cache中,所以查询缓存通常会和二级缓存一起使用。

<cache name="org.hibernate.cache.UpdateTimestampsCache" maxElementsInMemory="5000" eternal="true" overflowToDisk="false" />



2、开启二级缓存(以下属于hibernate属性,不管是直接hibernate还是spring的配置,都属于hibernate属性)
a、开启二级缓存(如果同时开启了查询缓存,查询缓存中有对应的数据时,二级缓存将不执行查询数据库操作)
hibernate.cache.provider_class=org.hibernate.cache.EhCacheProvider
hibernate.cache.use_second_level_cache=true
b、开启查询缓存
hibernate.cache.use_query_cache=true


二、二级缓存(不包括查询缓存),下面是几种配置方式(如果同时开启了查询缓存,查询缓存中有对应的数据时,二级缓存将不执行查询数据库操作):

1、直接配置实体类(这样的一个不好的地方就是,实体的xml 或者 注解方式的实体 ,增加了与实体本身无关的配置)
1.1、配置说明:
<cache usage="transactional|read-write|nonstrict-read-write|read-only" region="RegionName" include="all|non-lazy" />
usage(必须)说明了缓存的策略: transactional、 read-write、 nonstrict-read-write或 read-only。

region (可选, 默认为类或者集合的名字(class or collection role name)) 指定第二级缓存的区域名(name of the second level cache region)

include (可选,默认为 all) non-lazy 当属性级延迟抓取打开时, 标记为lazy="true"的实体的属性可能无法被缓存

*特别说明:如果要配置的实体有关联关系,1、对于 一对一 、多对一 ,只要将关联对象也配置为二级缓存就可以了,否则,只缓存当前实体,对于关联对象还是每次去数据库中查询的
2、 而对于 一对多 则需要 在 set 或 oneToMany上加 <cache usage="read-write"/> 或 @Cache(usage = CacheConcurrencyStrategy.READ_ONLY) ,这样也只加载 关联对象的id集合,要完全缓存,则需要另外 对关联的对象也配置成使用二级缓存)
3、对于多对多 则无法缓存关联对象,只能缓存当前对象,即使配置 上面 2 中的所说的也无济于事(反而配置了,第二次查询时 发出两条sql,不配置的话 发出一条sql)
如果使用了延迟加载,则关联对象不会使用缓存,也就是说上面的说法都是在,没有使用延迟加载的情况下才有效


1.2、hbm.xml 配置方式

<class name="MerchGroupBuy" table="merch_group_buy">
<!-- regionName:ehcache.xml 中 cache标签的name属性值,如果name属性值为 实体的包名.类名称,那么此处将不需要配置,建议使用实体的包名.类名称方式,避免一些问题 ,当然也可以代码中使用Query.setCacheRegion(paramString) 或 HibernateTemplate.setQueryCacheRegion(queryCacheRegion)-->
<cache usage="read-write" region="regionName" />
<id name="id">
<generator />
</id>
<property name="code" />
<property name="createTime" />
<property name="minNum" />
<property name="status"></property>
<property name="title" />
<property name="typeCode" />
<property name="updateTime" />
</class>

<!-- 如果 有关联的 一对多关系(这里只针对 一对多,而且仅仅缓存的是关联对象的id集合,如果需要实现完全缓存,则需要另外 对关联的对象也配置成使用二级缓存)-->

<set name="children" lazy="true" order-by="treeid asc">
<cache usage="read-write"/>
<key column="PARENTID"/>
<one-to-many eternal="false" timeToIdleSeconds="10"
timeToLiveSeconds="10" overflowToDisk="false" />

<!-- 用于保存查询最近查询的一系列表的时间戳 -->
<cache name="org.hibernate.cache.UpdateTimestampsCache" maxElementsInMemory="5000" eternal="true" overflowToDisk="false" />

<!-- 自定义查询缓存策略,需要在代码中使用Query.setCacheRegion("zsjQueryCache"); 或 HibernateTemplate.setQueryCacheRegion("zsjQueryCache"); -->
<cache name="zsjQueryCache"
maxElementsInMemory="50" eternal="false" timeToIdleSeconds="0"
timeToLiveSeconds="0" overflowToDisk="false" />

2、目前已经验证两种个途径支持 查询缓存

2.1、Criteria criteria = session.createCriteria(this.val$entityClass);
criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
HibernateTemplate.this.prepareCriteria(criteria);//prepareCriteria会设置 是不是使用查询缓存 ,criteria.setCacheable(true);
return criteria.list();


2.2、Query queryObject = session.createQuery(this.val$queryString);
HibernateTemplate.this.prepareQuery(queryObject);//prepareQuery会设置 是不是使用查询缓存 ,queryObject.setCacheable(true);
if (this.val$values != null) {
for (int i = 0; i < this.val$values.length; ++i) {
queryObject.setParameter(i, this.val$values[i]);
}
}
return queryObject.list();






2.3另外 使用HibernateTemplate的时候,你看看方法的执行过程 如果中间有类似于下面的判断的都会支持查询缓存(下面的方式是HibernateTemplate专门用来设置查询缓存的地方):

protected void prepareQuery(Query queryObject)
{
if (isCacheQueries()) {
queryObject.setCacheable(true);
if (getQueryCacheRegion() != null) {
queryObject.setCacheRegion(getQueryCacheRegion());
}
}
if (getFetchSize() > 0) {
queryObject.setFetchSize(getFetchSize());
}
if (getMaxResults() > 0) {
queryObject.setMaxResults(getMaxResults());
}
SessionFactoryUtils.applyTransactionTimeout(queryObject, getSessionFactory());
}


需要注意的是:
1、要开启查询缓存:hibernate.cache.use_query_cache=true

2、查询缓存一定要用 setCacheable(true) ,
3、如果要使用单独的缓存规则,则需要设置setCacheRegion(CacheRegion)

4、如果使用HibernateTemplate.setCacheQueries(true);和HibernateTemplate.setQueryCacheRegion("CacheRegion");的话 就不用每次使用query.setCacheable(true);了,HibernateTemplate会自己判断并且加上使用查询缓存



四、 二级缓存的管理

1、清楚二级缓存(还有更多evict开头的方法可以使用)
SessionFactory factory = HibernateUtils.getSessionFactory();
factory.evict(Student.class);
factory.evict(Student.class, 1);
factory.evictQueries()
factory.evictQueries(paramString)

2、查看 二级缓存数据
2.1、使用sessionFactory直接获取
Map cacheEntries = sessionFactory().getStatistics()
.getSecondLevelCacheStatistics("cacheRegionName") //其中 cacheRegionName 既是 ehcache.xml配置中的<cache 标签的name属性值
.getEntries();



2.2、让log4j打印缓存信息(生成环境下请注释掉,以免影响性能)
log4j.logger.org.hibernate.cache=debug


五、完整的ehcache.xml配置







<?xml version="1.0" encoding="GB2312"?>
<!--
<?xml version="1.0" encoding="UTF-8"?>
<ehcache name="ColorCache">
<defaultCache maxElementsInMemory="10000" eternal="false"
timeToIdleSeconds="120" timeToLiveSeconds="120" overflowToDisk="true"
diskPersistent="false" diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU" />
<cache name="zxtcache" maxElementsInMemory="100"
eternal="false" timeToIdleSeconds="999999999" timeToLiveSeconds="99999999"
memoryStoreEvictionPolicy="LFU">
</cache>
</ehcache>
-->
<ehcache>
<diskStore path="java.io.tmpdir/acb" />

<!-- 二级缓存(不包括查询缓存)默认这个配置,即:没有为某个实体专门配置Cache时默认使用该配置-->
<defaultCache maxElementsInMemory="10000" eternal="true"
timeToIdleSeconds="10" timeToLiveSeconds="10" overflowToDisk="true"
diskPersistent="false" diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU" />

<!-- 自定义二级缓存, 需要在代码中使用Query.setCacheRegion("zsjQueryCache"); 或 HibernateTemplate.setQueryCacheRegion("zsjQueryCache");-->
<Cache name="zsjTowCache" maxElementsInMemory="10000" eternal="false"
timeToIdleSeconds="0" timeToLiveSeconds="0" overflowToDisk="true"
diskPersistent="false" diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU" />

<!-- 直接指定name为实体 包名称.类名称,这样实体就会直接使用这个策略,不用再实体中具体指定或 代码设置了 -->
<Cache name="com.eloomobile.appstore.entity.AppGoodsReversions" maxElementsInMemory="10000" eternal="false"
timeToIdleSeconds="0" timeToLiveSeconds="0" overflowToDisk="true"
diskPersistent="false" diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU" />



<Cache name="com.eloomobile.appstore.entity.One" maxElementsInMemory="10000" eternal="false"
timeToIdleSeconds="10" timeToLiveSeconds="10" overflowToDisk="true"
diskPersistent="false" diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU" />

<Cache name="com.eloomobile.appstore.entity.Many" maxElementsInMemory="10000" eternal="false"
timeToIdleSeconds="10" timeToLiveSeconds="10" overflowToDisk="true"
diskPersistent="false" diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU" />


<Cache name="com.eloomobile.appstore.entity.demo.UserTo" maxElementsInMemory="10000" eternal="false"
timeToIdleSeconds="0" timeToLiveSeconds="0" overflowToDisk="true"
diskPersistent="false" diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU" />


<!-- 以下配置的都是查询缓冲 ,查询缓存(不包括二级缓存)默认这个配置,即:没有为某个实体专门配置Cache时默认使用该配置-->
<cache name="org.hibernate.cache.StandardQueryCache"
maxElementsInMemory="50" eternal="false" timeToIdleSeconds="10"
timeToLiveSeconds="10" overflowToDisk="false" />

<!-- 用于保存查询最近查询的一系列表的时间戳 -->
<cache name="org.hibernate.cache.UpdateTimestampsCache" maxElementsInMemory="5000" eternal="true" overflowToDisk="false" />

<!-- 自定义查询缓存策略,需要在代码中使用Query.setCacheRegion("zsjQueryCache"); 或 HibernateTemplate.setQueryCacheRegion("zsjQueryCache"); -->
<cache name="zsjQueryCache"
maxElementsInMemory="50" eternal="false" timeToIdleSeconds="0"
timeToLiveSeconds="0" overflowToDisk="false" />
</ehcache>

读书人网 >软件架构设计

热点推荐