- 浏览: 27754 次
- 性别:
- 来自: 北京
文章分类
最新评论
1. Cache简介:
缓存(Cache )是计算机领域非常通用的概念。它介于应用程序和永久性数据存储源(如硬盘上的文件或者数据库)之间,其作用是降低应用程序直接读写永久性数据存储源的频率,从而提高应用的运行性能。缓存中的数据是数据存储源中数据的拷贝,应用程序在运行时直接读写缓存中的数据,只在某些特定时刻按照缓存中的数据来同步更新数据存储源。
缓存的物理介质通常是内存,而永久性数据存储源的物理介质通常是硬盘或磁盘,应用程序读写内在的速度显然比读写硬盘的速度快,如果缓存中存放的数据量非常大,也会用硬盘作为缓存的物理介质。
缓存的实现不仅需要作为物理介质的硬件,同时还需要用于管理缓存的并发访问和过期等策略的软件。因此,缓存是通过软件和硬件共同实现的。
1.1. 持久化层的缓存的范围
缓存的范围决定了缓存的生命周期以及可以被谁访问。缓存的范围分为三类。
1) 事务范围:缓存只能被当前事务访问。缓存的生命周期依赖于事务的生命周期,当事务结束时,缓存也就结束生命周期。在此范围下,缓存的介质是内存。事务可以是数据库事务或者应用事务,每个事务都有独自的缓存,缓存内的数据通常采用相互关联的对象形式。
2) 进程范围:缓存被进程内的所有事务共享。这些事务有可能是并发访问缓存,因此必须对缓存采取必要的事务隔离机制。缓存的生命周期依赖于进程的生命周期,进程结束时,缓存也就结束了生命周期。进程范围的缓存可能会存放大量的数据,所以存放的介质可以是内存或硬盘。缓存内的数据既可以是相互关联的对象形式也可以是对象的松散数据形式。松散的对象数据形式有点类似于对象的序列化数据,但是对象分解为松散的算法比对象序列化的算法要求更快。
3) 集群范围:在集群环境中,缓存被一个机器或者多个机器的进程共享。缓存中的数据被复制到集群环境中的每个进程节点,进程间通过远程通信来保证缓存中的数据的一致性,缓存中的数据通常采用对象的松散数据形式。
对大多数应用来说,应该慎重地考虑是否需要使用集群范围的缓存,因为访问的速度不一定会比直接访问数据库数据的速度快多少。
持久化层可以提供多种范围的缓存。如果在事务范围的缓存中没有查到相应的数据,还可以到进程范围或集群范围的缓存内查询,如果还是没有查到,那么只有到数据库中查询。事务范围的缓存是持久化层的第一级缓存,通常它是必需的;进程范围或集群范围的缓存是持久化层的第二级缓存,通常是可选的。
1.2. 持久化层的缓存的并发访问策略
当多个并发的事务同时访问持久化层的缓存的相同数据时,会引起并发问题,必须采用必要的事务隔离措施。
在进程范围或集群范围的缓存,即第二级缓存,会出现并发问题。因此可以设定以下四种类型的并发访问策略,每一种策略对应一种事务隔离级别。
1) 事务型(Transactional)策略:仅仅在受管理环境中适用。它提供了Repeatable Read事务隔离级别。对于经常被读但很少修改的数据,可以采用这种隔离类型,因为它可以防止脏读和不可重复读这类的并发问题。
2) 读写型(read-write)策略:提供了Read Committed事务隔离级别。仅仅在非集群的环境中适用。对于经常被读但很少修改的数据,可以采用这种隔离类型,因为它可以防止脏读这类的并发问题。
3) 非严格读写型(nonstrict-read-write)策略:不保证缓存与数据库中数据的一致性。如果存在两个事务同时访问缓存中相同数据的可能,必须为该数据配置一个很短的数据过期时间,从而尽量避免脏读。对于极少被修改,并且允许偶尔脏读的数据,可以采用这种并发访问策略。
4) 只读型策略(read-only):对于从来不会修改的数据,如参考数据,可以使用这种并发访问策略。
事务型并发访问策略是事务隔离级别最高,只读型的隔离级别最低。事务隔离级别越高,并发性能就越低。
2. Hibernate中的缓存:
Hibernate中提供了两级Cache,第一级别的缓存是Session级别的缓存,它是属于事务范围的缓存。这一级别的缓存由hibernate管理的,一般情况下无需进行干预;第二级别的缓存是SessionFactory级别的缓存,它是属于进程范围或群集范围的缓存。这一级别的缓存可以进行配置和更改,并且可以动态加载和卸载。
Hibernate还为查询结果提供了一个查询缓存,它依赖于第二级缓存。
2.1. 一级缓存和二级缓存的比较:
第一级缓存 第二级缓存
存放数据的形式 相互关联的持久化对象 对象的散装数据
缓存的范围 事务范围,每个事务都有单独的第一级缓存 进程范围或集群范围,缓存被同一个进程或集群范围内的所有事务共享
并发访问策略 由于每个事务都拥有单独的第一级缓存,不会出现并发问题,无需提供并发访问策略 由于多个事务会同时访问第二级缓存中相同数据,因此必须提供适当的并发访问策略,来保证特定的事务隔离级别
数据过期策略 没有提供数据过期策略。处于一级缓存中的对象永远不会过期,除非应用程序显式清空缓存或者清除特定的对象 必须提供数据过期策略,如基于内存的缓存中的对象的最大数目,允许对象处于缓存中的最长时间,以及允许对象处于缓存中的最长空闲时间
物理存储介质 内存 内存和硬盘。对象的散装数据首先存放在基于内在的缓存中,当内存中对象的数目达到数据过期策略中指定上限时,就会把其余的对象写入基于硬盘的缓存中。
缓存的软件实现 在Hibernate的Session的实现中包含了缓存的实现 由第三方提供,Hibernate仅提供了缓存适配器(CacheProvider)。用于把特定的缓存插件集成到Hibernate中。
启用缓存的方式 只要应用程序通过Session接口来执行保存、更新、删除、加载和查询数据库数据的操作,Hibernate就会启用第一级缓存,把数据库中的数据以对象的形式拷贝到缓存中,对于批量更新和批量删除操作,如果不希望启用第一级缓存,可以绕过Hibernate API,直接通过JDBC API来执行指操作。 用户可以在单个类或类的单个集合的粒度上配置第二级缓存。如果类的实例被经常读但很少被修改,就可以考虑使用第二级缓存。只有为某个类或集合配置了第二级缓存,Hibernate在运行时才会把它的实例加入到第二级缓存中。
用户管理缓存的方式 第一级缓存的物理介质为内存,由于内存容量有限,必须通过恰当的检索策略和检索方式来限制加载对象的数目。Session的evit()方法可以显式清空缓存中特定对象,但这种方法不值得推荐。 第二级缓存的物理介质可以是内存和硬盘,因此第二级缓存可以存放大量的数据,数据过期策略的maxElementsInMemory属性值可以控制内存中的对象数目。管理第二级缓存主要包括两个方面:选择需要使用第二级缓存的持久类,设置合适的并发访问策略:选择缓存适配器,设置合适的数据过期策略。
2.2. 一级缓存的管理:
当应用程序调用Session的save()、update()、savaeOrUpdate()、get()或load(),以及调用查询接口的list()、iterate()或filter()方法时,如果在Session缓存中还不存在相应的对象,Hibernate就会把该对象加入到第一级缓存中。当清理缓存时,Hibernate会根据缓存中对象的状态变化来同步更新数据库。
Session为应用程序提供了两个管理缓存的方法:
evict(Object obj):从缓存中清除参数指定的持久化对象。
clear():清空缓存中所有持久化对象。
2.3. 二级缓存的管理:
2.3.1. Hibernate的二级缓存策略的一般过程如下:
1) 条件查询的时候,总是发出一条select * from table_name where …. (选择所有字段)这样的SQL语句查询数据库,一次获得所有的数据对象。
2) 把获得的所有数据对象根据ID放入到第二级缓存中。
3) 当Hibernate根据ID访问数据对象的时候,首先从Session一级缓存中查;查不到,如果配置了二级缓存,那么从二级缓存中查;查不到,再查询数据库,把结果按照ID放入到缓存。
4) 删除、更新、增加数据的时候,同时更新缓存。
Hibernate的二级缓存策略,是针对于ID查询的缓存策略,对于条件查询则毫无作用。为此,Hibernate提供了针对条件查询的Query Cache。
2.3.2. 什么样的数据适合存放到第二级缓存中?
1 很少被修改的数据
2 不是很重要的数据,允许出现偶尔并发的数据
3 不会被并发访问的数据
4 参考数据,指的是供应用参考的常量数据,它的实例数目有限,它的实例会被许多其他类的实例引用,实例极少或者从来不会被修改。
2.3.3. 不适合存放到第二级缓存的数据?
1 经常被修改的数据
2 财务数据,绝对不允许出现并发
3 与其他应用共享的数据。
2.3.4. 常用的缓存插件
Hibernater 的二级缓存是一个插件,下面是几种常用的缓存插件:
l EhCache:可作为进程范围的缓存,存放数据的物理介质可以是内存或硬盘,对Hibernate的查询缓存提供了支持。
l OSCache:可作为进程范围的缓存,存放数据的物理介质可以是内存或硬盘,提供了丰富的缓存数据过期策略,对Hibernate的查询缓存提供了支持。
l SwarmCache:可作为群集范围内的缓存,但不支持Hibernate的查询缓存。
l JBossCache:可作为群集范围内的缓存,支持事务型并发访问策略,对Hibernate的查询缓存提供了支持。
2.3.5. 配置二级缓存的主要步骤:
1) 选择需要使用二级缓存的持久化类,设置它的命名缓存的并发访问策略。这是最值得认真考虑的步骤。
2) 选择合适的缓存插件,然后编辑该插件的配置文件。
2.4. 使用EhCache配置二级缓存:
2.4.1. 配置准备:
1) 把ehcache-1.2.3.jar加入到当前应用的classpath中。
2) 在hibernate.cfg.xml文件中加入EhCache缓存插件的提供类。
< !--配置缓存插件 -->
< property name="hibernate.cache.provider_class">
org.hibernate.cache.EhCacheProvider
< /property>
3) 挎贝ehcache.xml文件到类路径(项目工程的src目录下),这个文件在Hibernate安装目录的etc下。
2.4.2. 配置步骤:
Hibernate允许在类和集合的粒度上设置第二级缓存。在映射文件中,< class>和< set>元素都有一个< cache>子元素,这个子元素用来配置二级缓存。
示例:以category(产品类别)和product(产品)的映射为例:
1) 修改要配置缓存的那个持久化类的对象关系映射文件:
Category.hbm.xml
< ?xml version="1.0" encoding="utf-8"?>
< !DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
< hibernate-mapping>
< class name="org.qiujy.domain.cachedemo.Category" table="categories">
< !?
配置缓存,必须紧跟在class元素后面
对缓存中的Category对象采用读写型的并发访问策略
-->
< cache usage="read-write"/>
< id name="id" type="java.lang.Long">
< column name="id" />
< generator class="native" />
< /id>
< !-- 配置版本号,必须紧跟在id元素后面 -->
< version name="version" column="version" type="java.lang.Long" />
< property name="name" type="java.lang.String">
< column name="name" length="32" not-null="true"/>
< /property>
< property name="description" type="java.lang.String">
< column name="description" length="255"/>
< /property>
< set name="products" table="products" cascade="all" inverse="true">
< !-- Hibernate只会缓存对象的简单属性的值,
要缓存集合属性,必须在集合元素中也加入< cache>子元素
而Hibernate仅仅是把与当前持久对象关联的对象的OID存放到缓存中。
如果希望把整个关联的对象的所有数据都存入缓存,
则要在相应关联的对象的映射文件中配置< cache>元素
-->
< cache usage="read-write"/>
< key column="categoryId" not-null="true"/>
< one-to-many class="org.qiujy.domain.cachedemo.Product"/>
< /set>
< /class>
< /hibernate-mapping>
Product.hbm.xml
< ?xml version="1.0" encoding="utf-8"?>
< !DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
< hibernate-mapping>
< class name="org.qiujy.domain.cachedemo.Product" table="products">
< cache usage="read-write"/>
< id name="id" type="java.lang.Long">
< column name="id" />
< generator class="native" />
< /id>
< !-- 配置版本号,必须紧跟在id元素后面 -->
< version name="version" column="version" type="java.lang.Long" />
< property name="name" type="java.lang.String">
< column name="name" length="32" not-null="true"/>
< /property>
< property name="description" type="java.lang.String">
< column name="description" length="255"/>
< /property>
< property name="unitCost" type="java.lang.Double">
< column name="unitCost" />
< /property>
< property name="pubTime" type="java.util.Date">
< column name="pubTime" not-null="true" />
< /property>
< many-to-one name="category"
column="categoryId"
class="org.qiujy.domain.cachedemo.Category"
cascade="save-update"
not-null="true">
< /many-to-one>
< /class>
< /hibernate-mapping>
2) 编辑ehcache.xml文件:
< ehcache>
< diskStore path="c:\\ehcache\"/>
< defaultCache
maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
overflowToDisk="true"
/>
< !-- 设置Category类的缓存的数据过期策略 -->
< cache name="org.qiujy.domain.cachedemo.Category"
maxElementsInMemory="100"
eternal="true"
timeToIdleSeconds="0"
timeToLiveSeconds="0"
overflowToDisk="false"
/>
< !-- 设置Category类的products集合的缓存的数据过期策略 -->
< cache name="org.qiujy.domain.cachedemo.Category.products"
maxElementsInMemory="500"
eternal="false"
timeToIdleSeconds="300"
timeToLiveSeconds="600"
overflowToDisk="true"
/>
< cache name="org.qiujy.domain.cachedemo.Product"
maxElementsInMemory="500"
eternal="false"
timeToIdleSeconds="300"
timeToLiveSeconds="600"
overflowToDisk="true"
/>
< /ehcache>
配置的元素说明:
元素或属性 描述
< diskStore> 设置缓存数据文件的存放目录
< defaultCache> 设置缓存的默认数据过期策略
< cache> 设定具体的命名缓存的数据过期策略
每个命名缓存代表一个缓存区域,每个缓存区域有各自的数据过期策略。命名缓存机制使得用户能够在每个类以及类的每个集合的粒度上设置数据过期策略。
cache元素的属性
name 设置缓存的名字,它的取值为类的全限定名或类的集合的名字
maxInMemory 设置基于内存的缓存中可存放的对象最大数目
eternal 设置对象是否为永久的,true表示永不过期,此时将忽略timeToIdleSeconds和timeToLiveSeconds属性;
默认值是false
timeToIdleSeconds 设置对象空闲最长时间,超过这个时间,对象过期。当对象过期时,EHCache会把它从缓存中清除。
如果此值为0,表示对象可以无限期地处于空闲状态。
timeToLiveSeconds 设置对象生存最长时间,超过这个时间,对象过期。
如果此值为0,表示对象可以无限期地存在于缓存中。
overflowToDisk 设置基于内在的缓存中的对象数目达到上限后,是否把溢出的对象写到基于硬盘的缓存中
3) 写一测试类:
package org.qiujy.test.cache;
import java.util.List;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.qiujy.common.HibernateSessionFactory;
import org.qiujy.domain.cachedemo.Product;
public class TestCache {
public static void main(String[] args) {
//test cache.........
Session session2 = HibernateSessionFactory.getSession();
Transaction tx2 =null;
try{
tx2 = session2.beginTransaction();
List list = session2.createQuery("from Product").list();
for(int i = 0 ; i < list.size(); i++){
Product prod = (Product)list.get(i);
System.out.println(prod.getName());
}
tx2.commit();
}catch(HibernateException e){
if(tx2 != null){
tx2.rollback();
}
e.printStackTrace();
}finally{
HibernateSessionFactory.closeSession();
}
//-------------------
Session session3 = HibernateSessionFactory.getSession();
Transaction tx3 =null;
try{
tx3 = session3.beginTransaction();
Product prod = (Product)session3.get(Product.class, new Long(1));
System.out.println("从cache中得到,不执行SQL---" + prod.getName());
tx3.commit();
}catch(HibernateException e){
if(tx3 != null){
tx3.rollback();
}
e.printStackTrace();
}finally{
HibernateSessionFactory.closeSession();
}
}
}
首先数据库插入1000条产品记录和1条类别记录。此1000个产品都属于这一类别。然后执行以上测试类,在Session2中查询所有的产品,输出它的产品名,Session2会把这些数据加载到二级缓存中,由于有1000个对象,而配置中定义内存中只能存放500个,剩下的对象就会写到指定的磁盘目录中缓存起来。所以在磁盘相应位置可看到数据文件:
2.5. 查询缓存(Query Cache):
对于经常使用的查询语句,如果启用了查询缓存,当第一次执行查询语句时,Hibernate会把查询结果存放在第二缓存中。以后再次执行该查询语句时,只需从缓存中获得查询结果,从而提高查询性能。
2.5.1. 查询缓存适用于以下场合:
l 在应用程序运行时经常使用的查询语句。
l 很少对与查询语句关联的数据库数据进行插入、删除或更新操作。
2.5.2. Hibernate的Query缓存策略的过程如下:
1) Hibernate首先根据这些信息组成一个Query Key,Query Key包括条件查询的请求一般信息:SQL, SQL需要的参数,记录范围(起始位置rowStart,最大记录个数maxRows),等。
2) Hibernate根据这个Query Key到Query缓存中查找对应的结果列表。如果存在,那么返回这个结果列表;如果不存在,查询数据库,获取结果列表,把整个结果列表根据Query Key放入到Query缓存中。
3) Query Key中的SQL涉及到一些表名,如果这些表的任何数据发生修改、删除、增加等操作,这些相关的Query Key都要从缓存中清空。
只有当经常使用同样的参数进行查询时,这才会有些用处。
启用查询缓存的步骤:
1) 配置二级缓存:
Hibernate提供了三种和查询相关的缓存区域:
l 默认的查询缓存区域:org.hibernate.cache.StandardQueryCache
l 用户自定义的查询缓存区域:
l 时间戳缓存区域:org.hibernate.cache.UpdateTimestampCache
默认的查询缓存区域以及用户自定义的查询缓存区域都用于存放查询结果。而时间戳缓存区域存放了对与查询结果相关的表进行插入、更新或删除操作的时间戳。Hibernate通过时间戳缓存区域来判断被缓存的查询结果是否过期。所以,当应用程序对数据库的相关数据做了修改,Hibernate会自动刷新缓存的查询结果。但是如果其他应用程序对数据库的相关数据做了修改,则无法监测,此时必须由应用程序负责监测这一变化,然后手工刷新查询结果。Query接口的setForceCacheRefresh(true)可以手工刷新查询结果。
在ehcache.xml中添加如下配置:
< !-- 设置默认的查询缓存的数据过期策略 -->
< cache name="org.hibernate.cache.StandardQueryCache"
maxElementsInMemory="50"
eternal="false"
timeToIdleSeconds="3600"
timeToLiveSeconds="7200"
overflowToDisk="true"/>
< !-- 设置时间戳缓存的数据过期策略 -->
< cache name="org.hibernate.cache.UpdateTimestampsCache"
maxElementsInMemory="5000"
eternal="true"
overflowToDisk="true"/>
< !-- 设置自定义命名查询缓存customerQueries的数据过期策略 -->
< cache name="myCacheRegion"
maxElementsInMemory="1000"
eternal="false"
timeToIdleSeconds="300"
timeToLiveSeconds="600"
overflowToDisk="true"
/>
2) 打开查询缓存:在hibernate.cfg.xml添加如下配置
< !--启用查询缓存 -->
< property name="cache.use_query_cache">true< /property>
3) 在程序中使用:
虽然按以上设置好了查询缓存,但Hibernate在执行查询语句语句时仍不会启用查询缓存。对于希望启用查询缓存的查询语句,应该调用Query接口的setCacheeable(true)方法:
测试类如下:
package org.qiujy.test.cache;
import java.util.List;
import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.qiujy.common.HibernateSessionFactory;
import org.qiujy.domain.cachedemo.Product;
public class TessQueryCache {
public static void main(String[] args) {
Session session = HibernateSessionFactory.getSession();
Transaction tx =null;
try{
tx = session.beginTransaction();
Query query = session.createQuery("from Product");
//激活查询缓存
query.setCacheable(true);
//使用自定义的查询缓存区域,若不设置,则使用标准查询缓存区域
query.setCacheRegion("myCacheRegion");
List list = query.list();
for(int i = 0 ; i < list.size(); i++){
Product prod = (Product)list.get(i);
System.out.println(prod.getName());
}
tx.commit();
}catch(HibernateException e){
if(tx != null){
tx.rollback();
}
e.printStackTrace();
}finally{
HibernateSessionFactory.closeSession();
}
}
}
缓存(Cache )是计算机领域非常通用的概念。它介于应用程序和永久性数据存储源(如硬盘上的文件或者数据库)之间,其作用是降低应用程序直接读写永久性数据存储源的频率,从而提高应用的运行性能。缓存中的数据是数据存储源中数据的拷贝,应用程序在运行时直接读写缓存中的数据,只在某些特定时刻按照缓存中的数据来同步更新数据存储源。
缓存的物理介质通常是内存,而永久性数据存储源的物理介质通常是硬盘或磁盘,应用程序读写内在的速度显然比读写硬盘的速度快,如果缓存中存放的数据量非常大,也会用硬盘作为缓存的物理介质。
缓存的实现不仅需要作为物理介质的硬件,同时还需要用于管理缓存的并发访问和过期等策略的软件。因此,缓存是通过软件和硬件共同实现的。
1.1. 持久化层的缓存的范围
缓存的范围决定了缓存的生命周期以及可以被谁访问。缓存的范围分为三类。
1) 事务范围:缓存只能被当前事务访问。缓存的生命周期依赖于事务的生命周期,当事务结束时,缓存也就结束生命周期。在此范围下,缓存的介质是内存。事务可以是数据库事务或者应用事务,每个事务都有独自的缓存,缓存内的数据通常采用相互关联的对象形式。
2) 进程范围:缓存被进程内的所有事务共享。这些事务有可能是并发访问缓存,因此必须对缓存采取必要的事务隔离机制。缓存的生命周期依赖于进程的生命周期,进程结束时,缓存也就结束了生命周期。进程范围的缓存可能会存放大量的数据,所以存放的介质可以是内存或硬盘。缓存内的数据既可以是相互关联的对象形式也可以是对象的松散数据形式。松散的对象数据形式有点类似于对象的序列化数据,但是对象分解为松散的算法比对象序列化的算法要求更快。
3) 集群范围:在集群环境中,缓存被一个机器或者多个机器的进程共享。缓存中的数据被复制到集群环境中的每个进程节点,进程间通过远程通信来保证缓存中的数据的一致性,缓存中的数据通常采用对象的松散数据形式。
对大多数应用来说,应该慎重地考虑是否需要使用集群范围的缓存,因为访问的速度不一定会比直接访问数据库数据的速度快多少。
持久化层可以提供多种范围的缓存。如果在事务范围的缓存中没有查到相应的数据,还可以到进程范围或集群范围的缓存内查询,如果还是没有查到,那么只有到数据库中查询。事务范围的缓存是持久化层的第一级缓存,通常它是必需的;进程范围或集群范围的缓存是持久化层的第二级缓存,通常是可选的。
1.2. 持久化层的缓存的并发访问策略
当多个并发的事务同时访问持久化层的缓存的相同数据时,会引起并发问题,必须采用必要的事务隔离措施。
在进程范围或集群范围的缓存,即第二级缓存,会出现并发问题。因此可以设定以下四种类型的并发访问策略,每一种策略对应一种事务隔离级别。
1) 事务型(Transactional)策略:仅仅在受管理环境中适用。它提供了Repeatable Read事务隔离级别。对于经常被读但很少修改的数据,可以采用这种隔离类型,因为它可以防止脏读和不可重复读这类的并发问题。
2) 读写型(read-write)策略:提供了Read Committed事务隔离级别。仅仅在非集群的环境中适用。对于经常被读但很少修改的数据,可以采用这种隔离类型,因为它可以防止脏读这类的并发问题。
3) 非严格读写型(nonstrict-read-write)策略:不保证缓存与数据库中数据的一致性。如果存在两个事务同时访问缓存中相同数据的可能,必须为该数据配置一个很短的数据过期时间,从而尽量避免脏读。对于极少被修改,并且允许偶尔脏读的数据,可以采用这种并发访问策略。
4) 只读型策略(read-only):对于从来不会修改的数据,如参考数据,可以使用这种并发访问策略。
事务型并发访问策略是事务隔离级别最高,只读型的隔离级别最低。事务隔离级别越高,并发性能就越低。
2. Hibernate中的缓存:
Hibernate中提供了两级Cache,第一级别的缓存是Session级别的缓存,它是属于事务范围的缓存。这一级别的缓存由hibernate管理的,一般情况下无需进行干预;第二级别的缓存是SessionFactory级别的缓存,它是属于进程范围或群集范围的缓存。这一级别的缓存可以进行配置和更改,并且可以动态加载和卸载。
Hibernate还为查询结果提供了一个查询缓存,它依赖于第二级缓存。
2.1. 一级缓存和二级缓存的比较:
第一级缓存 第二级缓存
存放数据的形式 相互关联的持久化对象 对象的散装数据
缓存的范围 事务范围,每个事务都有单独的第一级缓存 进程范围或集群范围,缓存被同一个进程或集群范围内的所有事务共享
并发访问策略 由于每个事务都拥有单独的第一级缓存,不会出现并发问题,无需提供并发访问策略 由于多个事务会同时访问第二级缓存中相同数据,因此必须提供适当的并发访问策略,来保证特定的事务隔离级别
数据过期策略 没有提供数据过期策略。处于一级缓存中的对象永远不会过期,除非应用程序显式清空缓存或者清除特定的对象 必须提供数据过期策略,如基于内存的缓存中的对象的最大数目,允许对象处于缓存中的最长时间,以及允许对象处于缓存中的最长空闲时间
物理存储介质 内存 内存和硬盘。对象的散装数据首先存放在基于内在的缓存中,当内存中对象的数目达到数据过期策略中指定上限时,就会把其余的对象写入基于硬盘的缓存中。
缓存的软件实现 在Hibernate的Session的实现中包含了缓存的实现 由第三方提供,Hibernate仅提供了缓存适配器(CacheProvider)。用于把特定的缓存插件集成到Hibernate中。
启用缓存的方式 只要应用程序通过Session接口来执行保存、更新、删除、加载和查询数据库数据的操作,Hibernate就会启用第一级缓存,把数据库中的数据以对象的形式拷贝到缓存中,对于批量更新和批量删除操作,如果不希望启用第一级缓存,可以绕过Hibernate API,直接通过JDBC API来执行指操作。 用户可以在单个类或类的单个集合的粒度上配置第二级缓存。如果类的实例被经常读但很少被修改,就可以考虑使用第二级缓存。只有为某个类或集合配置了第二级缓存,Hibernate在运行时才会把它的实例加入到第二级缓存中。
用户管理缓存的方式 第一级缓存的物理介质为内存,由于内存容量有限,必须通过恰当的检索策略和检索方式来限制加载对象的数目。Session的evit()方法可以显式清空缓存中特定对象,但这种方法不值得推荐。 第二级缓存的物理介质可以是内存和硬盘,因此第二级缓存可以存放大量的数据,数据过期策略的maxElementsInMemory属性值可以控制内存中的对象数目。管理第二级缓存主要包括两个方面:选择需要使用第二级缓存的持久类,设置合适的并发访问策略:选择缓存适配器,设置合适的数据过期策略。
2.2. 一级缓存的管理:
当应用程序调用Session的save()、update()、savaeOrUpdate()、get()或load(),以及调用查询接口的list()、iterate()或filter()方法时,如果在Session缓存中还不存在相应的对象,Hibernate就会把该对象加入到第一级缓存中。当清理缓存时,Hibernate会根据缓存中对象的状态变化来同步更新数据库。
Session为应用程序提供了两个管理缓存的方法:
evict(Object obj):从缓存中清除参数指定的持久化对象。
clear():清空缓存中所有持久化对象。
2.3. 二级缓存的管理:
2.3.1. Hibernate的二级缓存策略的一般过程如下:
1) 条件查询的时候,总是发出一条select * from table_name where …. (选择所有字段)这样的SQL语句查询数据库,一次获得所有的数据对象。
2) 把获得的所有数据对象根据ID放入到第二级缓存中。
3) 当Hibernate根据ID访问数据对象的时候,首先从Session一级缓存中查;查不到,如果配置了二级缓存,那么从二级缓存中查;查不到,再查询数据库,把结果按照ID放入到缓存。
4) 删除、更新、增加数据的时候,同时更新缓存。
Hibernate的二级缓存策略,是针对于ID查询的缓存策略,对于条件查询则毫无作用。为此,Hibernate提供了针对条件查询的Query Cache。
2.3.2. 什么样的数据适合存放到第二级缓存中?
1 很少被修改的数据
2 不是很重要的数据,允许出现偶尔并发的数据
3 不会被并发访问的数据
4 参考数据,指的是供应用参考的常量数据,它的实例数目有限,它的实例会被许多其他类的实例引用,实例极少或者从来不会被修改。
2.3.3. 不适合存放到第二级缓存的数据?
1 经常被修改的数据
2 财务数据,绝对不允许出现并发
3 与其他应用共享的数据。
2.3.4. 常用的缓存插件
Hibernater 的二级缓存是一个插件,下面是几种常用的缓存插件:
l EhCache:可作为进程范围的缓存,存放数据的物理介质可以是内存或硬盘,对Hibernate的查询缓存提供了支持。
l OSCache:可作为进程范围的缓存,存放数据的物理介质可以是内存或硬盘,提供了丰富的缓存数据过期策略,对Hibernate的查询缓存提供了支持。
l SwarmCache:可作为群集范围内的缓存,但不支持Hibernate的查询缓存。
l JBossCache:可作为群集范围内的缓存,支持事务型并发访问策略,对Hibernate的查询缓存提供了支持。
2.3.5. 配置二级缓存的主要步骤:
1) 选择需要使用二级缓存的持久化类,设置它的命名缓存的并发访问策略。这是最值得认真考虑的步骤。
2) 选择合适的缓存插件,然后编辑该插件的配置文件。
2.4. 使用EhCache配置二级缓存:
2.4.1. 配置准备:
1) 把ehcache-1.2.3.jar加入到当前应用的classpath中。
2) 在hibernate.cfg.xml文件中加入EhCache缓存插件的提供类。
< !--配置缓存插件 -->
< property name="hibernate.cache.provider_class">
org.hibernate.cache.EhCacheProvider
< /property>
3) 挎贝ehcache.xml文件到类路径(项目工程的src目录下),这个文件在Hibernate安装目录的etc下。
2.4.2. 配置步骤:
Hibernate允许在类和集合的粒度上设置第二级缓存。在映射文件中,< class>和< set>元素都有一个< cache>子元素,这个子元素用来配置二级缓存。
示例:以category(产品类别)和product(产品)的映射为例:
1) 修改要配置缓存的那个持久化类的对象关系映射文件:
Category.hbm.xml
< ?xml version="1.0" encoding="utf-8"?>
< !DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
< hibernate-mapping>
< class name="org.qiujy.domain.cachedemo.Category" table="categories">
< !?
配置缓存,必须紧跟在class元素后面
对缓存中的Category对象采用读写型的并发访问策略
-->
< cache usage="read-write"/>
< id name="id" type="java.lang.Long">
< column name="id" />
< generator class="native" />
< /id>
< !-- 配置版本号,必须紧跟在id元素后面 -->
< version name="version" column="version" type="java.lang.Long" />
< property name="name" type="java.lang.String">
< column name="name" length="32" not-null="true"/>
< /property>
< property name="description" type="java.lang.String">
< column name="description" length="255"/>
< /property>
< set name="products" table="products" cascade="all" inverse="true">
< !-- Hibernate只会缓存对象的简单属性的值,
要缓存集合属性,必须在集合元素中也加入< cache>子元素
而Hibernate仅仅是把与当前持久对象关联的对象的OID存放到缓存中。
如果希望把整个关联的对象的所有数据都存入缓存,
则要在相应关联的对象的映射文件中配置< cache>元素
-->
< cache usage="read-write"/>
< key column="categoryId" not-null="true"/>
< one-to-many class="org.qiujy.domain.cachedemo.Product"/>
< /set>
< /class>
< /hibernate-mapping>
Product.hbm.xml
< ?xml version="1.0" encoding="utf-8"?>
< !DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
< hibernate-mapping>
< class name="org.qiujy.domain.cachedemo.Product" table="products">
< cache usage="read-write"/>
< id name="id" type="java.lang.Long">
< column name="id" />
< generator class="native" />
< /id>
< !-- 配置版本号,必须紧跟在id元素后面 -->
< version name="version" column="version" type="java.lang.Long" />
< property name="name" type="java.lang.String">
< column name="name" length="32" not-null="true"/>
< /property>
< property name="description" type="java.lang.String">
< column name="description" length="255"/>
< /property>
< property name="unitCost" type="java.lang.Double">
< column name="unitCost" />
< /property>
< property name="pubTime" type="java.util.Date">
< column name="pubTime" not-null="true" />
< /property>
< many-to-one name="category"
column="categoryId"
class="org.qiujy.domain.cachedemo.Category"
cascade="save-update"
not-null="true">
< /many-to-one>
< /class>
< /hibernate-mapping>
2) 编辑ehcache.xml文件:
< ehcache>
< diskStore path="c:\\ehcache\"/>
< defaultCache
maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
overflowToDisk="true"
/>
< !-- 设置Category类的缓存的数据过期策略 -->
< cache name="org.qiujy.domain.cachedemo.Category"
maxElementsInMemory="100"
eternal="true"
timeToIdleSeconds="0"
timeToLiveSeconds="0"
overflowToDisk="false"
/>
< !-- 设置Category类的products集合的缓存的数据过期策略 -->
< cache name="org.qiujy.domain.cachedemo.Category.products"
maxElementsInMemory="500"
eternal="false"
timeToIdleSeconds="300"
timeToLiveSeconds="600"
overflowToDisk="true"
/>
< cache name="org.qiujy.domain.cachedemo.Product"
maxElementsInMemory="500"
eternal="false"
timeToIdleSeconds="300"
timeToLiveSeconds="600"
overflowToDisk="true"
/>
< /ehcache>
配置的元素说明:
元素或属性 描述
< diskStore> 设置缓存数据文件的存放目录
< defaultCache> 设置缓存的默认数据过期策略
< cache> 设定具体的命名缓存的数据过期策略
每个命名缓存代表一个缓存区域,每个缓存区域有各自的数据过期策略。命名缓存机制使得用户能够在每个类以及类的每个集合的粒度上设置数据过期策略。
cache元素的属性
name 设置缓存的名字,它的取值为类的全限定名或类的集合的名字
maxInMemory 设置基于内存的缓存中可存放的对象最大数目
eternal 设置对象是否为永久的,true表示永不过期,此时将忽略timeToIdleSeconds和timeToLiveSeconds属性;
默认值是false
timeToIdleSeconds 设置对象空闲最长时间,超过这个时间,对象过期。当对象过期时,EHCache会把它从缓存中清除。
如果此值为0,表示对象可以无限期地处于空闲状态。
timeToLiveSeconds 设置对象生存最长时间,超过这个时间,对象过期。
如果此值为0,表示对象可以无限期地存在于缓存中。
overflowToDisk 设置基于内在的缓存中的对象数目达到上限后,是否把溢出的对象写到基于硬盘的缓存中
3) 写一测试类:
package org.qiujy.test.cache;
import java.util.List;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.qiujy.common.HibernateSessionFactory;
import org.qiujy.domain.cachedemo.Product;
public class TestCache {
public static void main(String[] args) {
//test cache.........
Session session2 = HibernateSessionFactory.getSession();
Transaction tx2 =null;
try{
tx2 = session2.beginTransaction();
List list = session2.createQuery("from Product").list();
for(int i = 0 ; i < list.size(); i++){
Product prod = (Product)list.get(i);
System.out.println(prod.getName());
}
tx2.commit();
}catch(HibernateException e){
if(tx2 != null){
tx2.rollback();
}
e.printStackTrace();
}finally{
HibernateSessionFactory.closeSession();
}
//-------------------
Session session3 = HibernateSessionFactory.getSession();
Transaction tx3 =null;
try{
tx3 = session3.beginTransaction();
Product prod = (Product)session3.get(Product.class, new Long(1));
System.out.println("从cache中得到,不执行SQL---" + prod.getName());
tx3.commit();
}catch(HibernateException e){
if(tx3 != null){
tx3.rollback();
}
e.printStackTrace();
}finally{
HibernateSessionFactory.closeSession();
}
}
}
首先数据库插入1000条产品记录和1条类别记录。此1000个产品都属于这一类别。然后执行以上测试类,在Session2中查询所有的产品,输出它的产品名,Session2会把这些数据加载到二级缓存中,由于有1000个对象,而配置中定义内存中只能存放500个,剩下的对象就会写到指定的磁盘目录中缓存起来。所以在磁盘相应位置可看到数据文件:
2.5. 查询缓存(Query Cache):
对于经常使用的查询语句,如果启用了查询缓存,当第一次执行查询语句时,Hibernate会把查询结果存放在第二缓存中。以后再次执行该查询语句时,只需从缓存中获得查询结果,从而提高查询性能。
2.5.1. 查询缓存适用于以下场合:
l 在应用程序运行时经常使用的查询语句。
l 很少对与查询语句关联的数据库数据进行插入、删除或更新操作。
2.5.2. Hibernate的Query缓存策略的过程如下:
1) Hibernate首先根据这些信息组成一个Query Key,Query Key包括条件查询的请求一般信息:SQL, SQL需要的参数,记录范围(起始位置rowStart,最大记录个数maxRows),等。
2) Hibernate根据这个Query Key到Query缓存中查找对应的结果列表。如果存在,那么返回这个结果列表;如果不存在,查询数据库,获取结果列表,把整个结果列表根据Query Key放入到Query缓存中。
3) Query Key中的SQL涉及到一些表名,如果这些表的任何数据发生修改、删除、增加等操作,这些相关的Query Key都要从缓存中清空。
只有当经常使用同样的参数进行查询时,这才会有些用处。
启用查询缓存的步骤:
1) 配置二级缓存:
Hibernate提供了三种和查询相关的缓存区域:
l 默认的查询缓存区域:org.hibernate.cache.StandardQueryCache
l 用户自定义的查询缓存区域:
l 时间戳缓存区域:org.hibernate.cache.UpdateTimestampCache
默认的查询缓存区域以及用户自定义的查询缓存区域都用于存放查询结果。而时间戳缓存区域存放了对与查询结果相关的表进行插入、更新或删除操作的时间戳。Hibernate通过时间戳缓存区域来判断被缓存的查询结果是否过期。所以,当应用程序对数据库的相关数据做了修改,Hibernate会自动刷新缓存的查询结果。但是如果其他应用程序对数据库的相关数据做了修改,则无法监测,此时必须由应用程序负责监测这一变化,然后手工刷新查询结果。Query接口的setForceCacheRefresh(true)可以手工刷新查询结果。
在ehcache.xml中添加如下配置:
< !-- 设置默认的查询缓存的数据过期策略 -->
< cache name="org.hibernate.cache.StandardQueryCache"
maxElementsInMemory="50"
eternal="false"
timeToIdleSeconds="3600"
timeToLiveSeconds="7200"
overflowToDisk="true"/>
< !-- 设置时间戳缓存的数据过期策略 -->
< cache name="org.hibernate.cache.UpdateTimestampsCache"
maxElementsInMemory="5000"
eternal="true"
overflowToDisk="true"/>
< !-- 设置自定义命名查询缓存customerQueries的数据过期策略 -->
< cache name="myCacheRegion"
maxElementsInMemory="1000"
eternal="false"
timeToIdleSeconds="300"
timeToLiveSeconds="600"
overflowToDisk="true"
/>
2) 打开查询缓存:在hibernate.cfg.xml添加如下配置
< !--启用查询缓存 -->
< property name="cache.use_query_cache">true< /property>
3) 在程序中使用:
虽然按以上设置好了查询缓存,但Hibernate在执行查询语句语句时仍不会启用查询缓存。对于希望启用查询缓存的查询语句,应该调用Query接口的setCacheeable(true)方法:
测试类如下:
package org.qiujy.test.cache;
import java.util.List;
import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.qiujy.common.HibernateSessionFactory;
import org.qiujy.domain.cachedemo.Product;
public class TessQueryCache {
public static void main(String[] args) {
Session session = HibernateSessionFactory.getSession();
Transaction tx =null;
try{
tx = session.beginTransaction();
Query query = session.createQuery("from Product");
//激活查询缓存
query.setCacheable(true);
//使用自定义的查询缓存区域,若不设置,则使用标准查询缓存区域
query.setCacheRegion("myCacheRegion");
List list = query.list();
for(int i = 0 ; i < list.size(); i++){
Product prod = (Product)list.get(i);
System.out.println(prod.getName());
}
tx.commit();
}catch(HibernateException e){
if(tx != null){
tx.rollback();
}
e.printStackTrace();
}finally{
HibernateSessionFactory.closeSession();
}
}
}
相关推荐
二、LoadingCache 简介 LoadingCache 是 Guava 库提供的一种缓存实现方式,继承自 Cache 接口。LoadingCache 提供了自动加载缓存项的功能,当缓存项不存在时,通过 CacheLoader 加载缓存项。 三、基于 Loading...
InterSystems Cache简介** InterSystems是一家全球领先的健康信息技术和关键应用供应商,其Cache产品是其主打的数据库系统之一。Cache以其强大的性能和可扩展性闻名,尤其在实时数据处理和分布式系统中表现出色。...
【Cache简介】 缓存(Cache)在IT领域中扮演着至关重要的角色,它是一种提高系统性能、降低硬件需求的关键技术。本文将重点介绍Jboss Cache及其特性,同时提及Tangosol,以及如何将缓存集成到项目中。 **优势...
2. **Doctrine Cache简介** Doctrine Cache是一个轻量级且易于使用的组件,它为PHP开发人员提供了丰富的API,用于管理和操作各种类型的缓存。通过这个库,开发者可以快速实现对数据的缓存,并且能够灵活地选择不同...
JBoss Cache简介 - **复制与持久化**: 支持复制和持久化,提供透明的细粒度缓存。 - **多环境适应**: 可在任何Java标准版环境中运行,如应用服务器内或独立程序中。 - **应用示例**: 用于EJB 3.0的stateful session...
##### 2.1 Cache简介 Cache是一种高速缓冲存储器,通常位于CPU与主存储器之间。它的主要目的是缩短CPU获取指令和数据的时间,从而提高系统的整体性能。由于CPU访问主存储器的速度远低于其自身的处理速度,因此通过...
#### 一、Spring Cache简介与特性 Spring Cache 是 Spring 3.1 版本引入的一项重要特性,它不是一种具体的缓存实现(如 EHCache 或 OSCache),而是一种缓存使用的抽象层。通过在现有的业务代码上添加特定的注解,...
在第1部分,XtremSW Cache基本信息中,第1章“管理XtremSW Cache简介”向读者展示了XtremSW Cache的核心功能和价值。它是一个内存级的缓存系统,旨在减少存储系统的延迟,显著提高应用程序的性能。通过将频繁访问的...
MMU 和 Cache 简介 MMU(Memory Management Unit,存储管理单元)是一种硬件机制,用于管理存储系统,实现虚拟地址到物理地址的映射。 Cache 则是一种高速缓存机制,用于加速存储器件的访问速度。 在嵌入式系统中...
Cache 原理简介 Cache 是一种高速缓冲存储器,位于 CPU 和主存储器之间,用于减少 CPU 访问主存储器的时间。 Cache 的设计基于局部性原理,即 CPU 访问的数据倾向于集中在某个小范围内。 存储器的层次结构中, ...
一、SpringCache简介 SpringCache是Spring框架提供的一个轻量级的缓存抽象层,它允许开发者通过简单的注解方式在应用程序中引入缓存功能,支持多种缓存实现,包括 Ehcache、Hazelcast 和 Redis 等。 二、Redis介绍 ...
一、Spring Boot与Guava Cache简介 1. Spring Boot:作为Spring框架的微服务启动器,Spring Boot简化了Java应用的初始化和配置,通过预设默认配置,使得开发者可以快速构建健壮的Web应用。 2. Guava Cache:Google...
#### 一、Oracle In-Memory Database Cache 简介 Oracle In-Memory Database Cache(通常称为TimesTen)是Oracle提供的一种高性能、低延迟的内存数据库解决方案。该技术特别适用于需要高速数据处理的应用场景,如...
cache原理简介 Cache 是计算机系统中的一种高速缓存存储器,主要用于解决内存存储器的访问速度问题。Cache 的主要特点是,它可以快速地访问数据,但是容量有限。Cache 的原理可以用存储器的层次结构来描述,即从 ...
高速缓冲存贮器(Cache)是一种特殊的存储设备,它的设计目标是解决计算机系统中处理器(CPU)速度与主存储器(Main Memory)速度之间存在的显著差异问题。由于CPU的运算速度远超主存的访问速度,Cache的存在可以极...
简介: 开发过程中遇到一个问题,不同的浏览器对HTML的缓存的内容不一样,导致客户端按回退/前进/刷新按钮时,不同浏览器看到的查询结果是不一样的。Google,Baidu以后,找到这份最完整的关于Cache...
### Laravel页面缓存简介 Laravel内置了多种缓存驱动,如文件、数据库、Redis、Memcached等,这些驱动主要针对数据查询结果的缓存。而页面缓存则是对整个HTTP响应进行缓存,包括视图、布局、部分视图以及数据库查询...
johnny-cache简介 johnny-cache是一款针对Django设计的高性能缓存解决方案。它不仅提供了对Django内置缓存系统的增强,还引入了“脏”对象跟踪机制,自动检测并更新缓存中的过期数据。johnny-cache的主要特点包括...
- `README`或`README.md`:项目简介和使用指南。 - `LICENSE`:软件许可协议。 - `tests`目录:单元测试和集成测试代码。 - `docs`或`doc`目录:可能包含项目的文档和API参考。 在实际使用`diskcache`时,开发者...