- 浏览: 93413 次
- 性别:
- 来自: baga
最新评论
-
nicegege:
getContextPath、getServletPath、getRequestURI的区别 -
w156445045:
请问下博主,现在最新的iReport也不能调用存储过程嘛?
i ...
用ireport调用oracle存储过程 -
how:
function传入参数可以吗?
用ireport调用oracle存储过程 -
axxxx2000:
一个事物一个版本记录下
求教,上海怎么拿到7k以上薪水 -
holan:
axxxx2000 写道wl95421 写道今天刚面试完人,随 ...
求教,上海怎么拿到7k以上薪水
由于本人也是边翻边学,有什么翻译不当的地方在所难免,欢迎大家批评指正
原文题目:Speed Up Your Hibernate Application with Second-Level Caching
原文来源:http://www.devx.com/dbzone/Article/29685/1954
作者简介:John Ferguson Smart,参与过很多企业和政府大型的的J2EE项目,他的专长包括J2EE的架构,开发和IT项目管理。他也有很多的在JAVA的开源技术方面的经验。这是他技术blog的链接www.jroller.com/page/wakaleo
原文翻译如下:
通过二级缓存来加快你的hibernate应用程序
新的hibernate开发人员有时并不知道hibernate的缓存结果并没有充分的使用它,尽管如此,当我们正确使用缓存的时候,它能够变为加速hibernate应用程序最有力的武器之一。
在web应用程序中和大数据量的数据库交互经常导致性能问题。hibernate是一种高性能的,提供对象/关系持久化和查询的服务,但是如果没有帮助就不会解决所有性能上的问题。在很多情况下,二级缓存就是hibernate潜在的需要来实现所有的性能上的处理。这篇文章将研究hibernate的缓存功能并且展现怎么使用才能明显的提升应用程序的性能。
缓存介绍
缓存被广泛用于数据库应用领域。缓存的设计就是为了通过存储已经从数据库读取的数据来减少应用程序和数据库之间的数据流量,而数据库的访问只在检索的数据不在当前缓存的时候才需要。如果数据库以一些方式进行更新和修改的话,那么应用程序可能需要每隔一段时间清空缓存,因为它没有方法知道缓存里的数据是不是最新的。
Hibernate缓存
从对象来说,hibernate用了两种缓存的方法:一级缓存和二级缓存。一级缓存和Session对象关联,二级缓存和Session Factory对象关联。默认情况下,hibernate使用一级缓存来作为一次事务预处理的基础。hibernate使用它主要是为了减少在一次给定的事务中需要被生成的SQL查询语句。例如,一个对象在同一个事务中被修改好几次,hibernate会在事务结束的时候只生成一条SQL更新语句,它包含了所有修改内容。这篇文章主要介绍二级缓存。为了减少和数据库的交互,二级缓存保存已经读出的对象在各个事务之间的Session Factory级别。这些对象在整个程序中都可以得到,而不仅是用户运行查询的时候。这种方式,每次查询返回的对象都已经被载入了缓存,一次或多次潜在的数据库事务可以避免。
除此之外,你可以使用查询级别缓存如果你需要缓存的实际的查询结果,而不仅仅是持久化对象。
缓存的实现
缓存是软件中复杂的部分,市场上也提供了相当数量的选择,包括基于开源的和商业的。hibernate提供了以下开源的缓存实现,如下:
* EHCache (org.hibernate.cache.EhCacheProvider)
* OSCache (org.hibernate.cache.OSCacheProvider)
* SwarmCache (org.hibernate.cache.SwarmCacheProvider)
* JBoss TreeCache (org.hibernate.cache.TreeCacheProvider)
不同的缓存提供了在性能,内存使用和配置的可扩展性方面不同的能力
EHCache是一种快速的,轻量级的,容易上手的缓存。它支持只读和读写缓存,基于内存和硬盘的数据储存。但是,它不支持簇。
OSCache是另一个开源的缓存解决方案,它是一个也为jsp页面和任意对象提供缓存功能的大的开发包的一部分。它本身也是一个强大和灵活的开发包,像EHCache一样,也支持只读和读写缓存,基于内存和硬盘的数据储存。它通过JavaGroups或者JMS也提供了对簇的基本支持。
SwarmCache是一种基于簇的解决方案,它本身也基于集群服务实体间通信的通信协议。它提供了只读和没有限制的读写缓存(下一部分将解释这个名词)。这种类型的缓存对那些典型的读操作比写操作多的多的应用程序是适合的。
JBoss TreeCache是一种强大的两重性(同步的或异步的)和事务性的缓存。使用此实现如果你确实需要一种事务能力的缓存架构。
另一种值得提及的缓存实现是商业化的Tangosol Coherence cache。
缓存策略
一旦你选择了你的缓存实现,你需要指定你的缓存策略。以下是四种缓存策略:
只读:这种策略对那种数据被频繁读取但是不更新是有效的,这是到目前为止最简单,表现最好的缓存策略。
读写:读写缓存对假设你的数据需要更新是必要的。但是读写需要比只读缓存花费更多的资源。在非JTA的事务环境中,每一个事务需要完成在Session.close() 或Session.disconnect()被调用的时候。
没有限制的读写:这个策略不能保证2个事务不会同时的修改相同的数据。因此,它可能对那些数据经常被读取但只会偶尔进行写操作的最适合。
事务性:这是个完全事务性的缓存,它可能只能被用在JTA环境中。
对每一个缓存实现来说,支持策略不是唯一的。图一展现了对
缓存实现可供的选择。
文章余下的部分用来展示一个使用EHCache单一的JVM缓存。
缓存配置
为了启用二级缓存,我们需要在hibernate.cfg.xml配置文件中定义hibernate.cache.provider_class属性,如下
为了在hibernate3中测试的目的,你还要使用hibernate.cache.use_second_level_cache属性,它可以让你启用(和关闭)二级缓存。默认情况下,二级缓存是启用的同时使用EHCache。
一个实际应用
这个例子演示的程序包含四张简单的表:国家列表,机场列表,员工列表,语言列表。每个员工被分配了一个国家,并且能说很多语言。每个国家能有任意数量的机场。
图一展现了UML类图
图二展现了数据库架构
这个例子的源代码(http://assets.devx.com/sourcecode/14239.tgz)包括了以下的SQL脚本,你需要用它创建和实例化数据库。
* src/sql/create.sql: 创建数据库的SQL脚本
* src/sql/init.sql: 测试数据
安装Maven2
在写的时候,Maven2安装目录看来好像缺少了一些jars。为了解决这个问题,在应用程序源码的根目录里找到那些缺少的jars。把它们安装到Maven2的文件里,到应用程序的目录下,执行以下的命令
$ mvn install:install-file -DgroupId=javax.security -DartifactId=jacc
-Dversion=1.0
-Dpackaging=jar -Dfile=jacc-1.0.jar
$ mvn install:install-file -DgroupId=javax.transaction -DartifactId=jta -Dversion=1.0.1B
-Dpackaging=jar -Dfile=jta-1.0.1B.jar
建立一个只读缓存
从简单的开始,下面是country类的hibernate映射。
假设你要显示国家列表。你可以通过CountryDAO类中一个简单的方法实现,如下
因为这个方法经常被调用,所以你要知道它在压力下的行为。写一个简单的单元测试来模拟5次连续的调用。
你能够运行这个测试通过自己喜欢的IDE或者Maven2的命令行(演示程序提供了2个Maven2的工程文件)。这个演示程序通过本地的mysql来测试。当你运行这个测试的时候,应该得到类似以下的一些信息:
$mvn test -Dtest=CountryDAOTest
...
testGetCountries: 521 ms.
testGetCountries: 357 ms.
testGetCountries: 249 ms.
testGetCountries: 257 ms.
testGetCountries: 355 ms.
[surefire] Running com.wakaleo.articles.caching.dao.CountryDAOTest
[surefire] Tests run: 1, Failures: 0, Errors: 0, Time elapsed: 3,504 sec
可以看出每次调用大概花费半秒的时间,对大多数标准来说还是有点迟缓的。国家的列表很可能不是经常的改变,所以这个类可以作为只读缓存一个好的候选。所以加上去
你可以启用二级缓存用以下两种方法的任意一种
1.你可以在*.hbm.xml里启用它,使用cache的属性
2.你可以存储所有缓存信息在hibernate.cfg.xml文件中,使用class-cache属性
下一步,你需要为这个类设置缓存规则,这些规则决定了缓存怎么表现的细节。这个例子的演示是使用EHCache,但是记住每一种缓存实现是不一样的。
EHCache需要一个配置文件(通常叫做ehcache.xml)在类的根目录。EHCache配置文件的详细文档可以看这里(http://ehcache.sourceforge.net/documentation)。基本上,你要为每个需要缓存的类定义规则,以及一个defaultCache在你没有明确指明任何规则给一个类的时候使用。
对第一个例子来说,你可以使用下面简单的EHCache配置文件
这个文件为countries类建立一个基于内存最多300单位的缓存(countries类包含了229个国家)。注意缓存不会过期('eternal=true'属性)。现在通过返回的结果看下缓存的表现
$mvn test -Dtest=CompanyDAOTest
...
testGetCountries: 412 ms.
testGetCountries: 98 ms.
testGetCountries: 92 ms.
testGetCountries: 82 ms.
testGetCountries: 93 ms.
[surefire] Running com.wakaleo.articles.caching.dao.CountryDAOTest
[surefire] Tests run: 1, Failures: 0, Errors: 0, Time elapsed: 2,823 sec
正如你期盼的那样,第一次查询没有改变因为需要加载数据。但是,随后的几次查询就快多了。
后台
在我们继续之前,看下后台发生了什么非常有用。一件事情你需要知道的是hibernate缓存不储存对象实例,代替的是它储存对象“脱水”的形式(hibernate的术语),也就是作为一系列属性值。以下是一个countries缓存例子的内容
{
30 => [bw,Botswana,30],
214 => [uy,Uruguay,214],
158 => [pa,Panama,158],
31 => [by,Belarus,31]
95 => [in,India,95]
...
}
注意每个ID是怎么样映射到拥有属性值的数组的。你可能也注意到了只有主要的属性被储存了,而没有airports属性,这是因为airports属性只是一个关联:对其他持久化对象一系列的引用。
默认情况下,hibernate不缓存关联。而由你决定来缓存哪个关联,哪个关联需要被重载当缓存对象从二级缓存获得的时候。
关联缓存是一个非常强大的功能。下一部分我们将介绍更多的内容。
和关联缓存一起工作
假设你需要显示一个给定的国家的所有的员工(包括员工的名字,使用的语言等)。以下是employee类的hibernate映射
假设你每次使用employee对象的时候需要得到一个员工会说的语言。强逼hibernate自动载入关联的languages集合,你设置了lazy属性为false()。你还需要一个DAO类来得到所有employee,以下的代码来帮你实现
下一步,写一些简单的单元测试来看它怎么表现。正如前面的例子一样,你需要知道它被重复调用时候的性能
如果你运行上面的代码,你会得到类似以下的一些数据
$mvn test -Dtest=EmployeeDAOTest
...
testGetNZEmployees: 1227 ms.
testGetAUEmployees: 883 ms.
testGetNZEmployees: 907 ms.
testGetAUEmployees: 873 ms.
testGetNZEmployees: 987 ms.
testGetAUEmployees: 916 ms.
[surefire] Running com.wakaleo.articles.caching.dao.EmployeeDAOTest
[surefire] Tests run: 3, Failures: 0, Errors: 0, Time elapsed: 3,684 sec
所以对一个国家载入大约50个员工需要花费大约一秒的时间。这种方法显然太慢了。这是典型的N+1的查询问题。如果你启用SQL日志,你会发现对employee表的一次查询,紧跟着对language表几百次的查询,无论什么时候hibernate从缓存里得到一个employee对象,它都会重载所有关联的language。那怎么提升它的性能呢?第一件要做的事就是对employee启用读写缓存,如下
你还应该对language类启用缓存。只读缓存如下
然后你需要配置缓存的规则通过加入以下的内容到ehcache.xml文件中
但是还是没有解决N+1的查询问题:当你载入一个employee对象的时候大约50次的额外查询还是会执行。这里你就需要在Employee.hbm.xml映射文件里关联的language启用缓存,如下
通过这个配置,你就能得到几近最优的性能
$mvn test -Dtest=EmployeeDAOTest
...
testGetNZEmployees: 1477 ms.
testGetAUEmployees: 940 ms.
testGetNZEmployees: 65 ms.
testGetAUEmployees: 65 ms.
testGetNZEmployees: 76 ms.
testGetAUEmployees: 52 ms.
[surefire] Running com.wakaleo.articles.caching.dao.EmployeeDAOTest
[surefire] Tests run: 3, Failures: 0, Errors: 0, Time elapsed: 0,228 sec
查询缓存
在确定的情况下,缓存一次查询的正确结果是非常有用的,不仅是只是个确定的对象。例如,getCountries()方法在每次调用的时候或许会返回相同的国家列表。所以,除了缓存country类之外,你也应该缓存查询结果本身。
为了实现这个目标,你需要在hibernate.cfg.xml文件中设置hibernate.cache.use_query_cache属性为true,如下
<property name="hibernate.cache.use_query_cache">true</property>
然后需要在对任何查询缓存的时候使用setCacheable()方法,如下
但是,它不能预知到其他程序对数据库任何的改变。所以你不应该使用任何二级缓存(或为类和集合的缓存设置简短的过期时间)如果你的数据总是要保证最新的状态。
正确的使用hibernate缓存
缓存是一种强大的技术,hibernate提供了一种强大的,灵活的,不显眼的方式来实现它。即使对很多简单的例子来说默认的设置能使实际的性能得到提升。但是,像很多强大工具一样,hibernate还是需要一些思考和微调来得到最优的结果,而缓存像其他的优化技术一样,应该使用一种可扩展,测试驱动的方法所实现。当正确使用的时候,少量的缓存实现,能最大程度的提高你的程序运行。
请你自己写几个二级缓存的应用在自己的项目中,并作测试,你就知道怎么回事了。
jkfzero 写道
不久前因为用了内存缓存被狂BS,说服务器的内存是极为珍贵的资源……貌似数据库不是珍贵的资源?!
应该说是得看什么项目。比较倾向自己写的CACHE
这.. 那如果缓存里的东西跟数据库已经不一样勒捏?
而且,用这种缓存,内存很快就会爆炸的吧 ?
比方10亿条记录里有1亿条符合条件~
个人觉得优化数据库查询应该从数据库本身入手,
提供索引等等的优化方案。
没人让你把10亿条记录让你都缓存起来吧,
问题就在这,当你在一个项目中正确使用二级缓存以后,会发现这个时间早就能实现自己的根据业务需要特殊优化的缓存。
自己针对业务设计的缓存效率要远远超过Hibernate自己的二级缓存机制,因为Hibernate是一个通用机制,为了这个通用特性就得牺牲不少东西,包括内存使用空间是时间成本。
hibernate的二级缓存,我有一个问题,如果一个类的实例已被cache,则下次调用get方法时,其是先得到连接,再从cache中获取实例呢?还是先判断cache中是否有实例,如果不存在,得到连接从数据库中读取?
http://zem.iteye.com/blog/138683,这里有篇文章你可以参考下
不久前因为用了内存缓存被狂BS,说服务器的内存是极为珍贵的资源……
貌似数据库不是珍贵的资源?!
原文题目:Speed Up Your Hibernate Application with Second-Level Caching
原文来源:http://www.devx.com/dbzone/Article/29685/1954
作者简介:John Ferguson Smart,参与过很多企业和政府大型的的J2EE项目,他的专长包括J2EE的架构,开发和IT项目管理。他也有很多的在JAVA的开源技术方面的经验。这是他技术blog的链接www.jroller.com/page/wakaleo
原文翻译如下:
通过二级缓存来加快你的hibernate应用程序
新的hibernate开发人员有时并不知道hibernate的缓存结果并没有充分的使用它,尽管如此,当我们正确使用缓存的时候,它能够变为加速hibernate应用程序最有力的武器之一。
在web应用程序中和大数据量的数据库交互经常导致性能问题。hibernate是一种高性能的,提供对象/关系持久化和查询的服务,但是如果没有帮助就不会解决所有性能上的问题。在很多情况下,二级缓存就是hibernate潜在的需要来实现所有的性能上的处理。这篇文章将研究hibernate的缓存功能并且展现怎么使用才能明显的提升应用程序的性能。
缓存介绍
缓存被广泛用于数据库应用领域。缓存的设计就是为了通过存储已经从数据库读取的数据来减少应用程序和数据库之间的数据流量,而数据库的访问只在检索的数据不在当前缓存的时候才需要。如果数据库以一些方式进行更新和修改的话,那么应用程序可能需要每隔一段时间清空缓存,因为它没有方法知道缓存里的数据是不是最新的。
Hibernate缓存
从对象来说,hibernate用了两种缓存的方法:一级缓存和二级缓存。一级缓存和Session对象关联,二级缓存和Session Factory对象关联。默认情况下,hibernate使用一级缓存来作为一次事务预处理的基础。hibernate使用它主要是为了减少在一次给定的事务中需要被生成的SQL查询语句。例如,一个对象在同一个事务中被修改好几次,hibernate会在事务结束的时候只生成一条SQL更新语句,它包含了所有修改内容。这篇文章主要介绍二级缓存。为了减少和数据库的交互,二级缓存保存已经读出的对象在各个事务之间的Session Factory级别。这些对象在整个程序中都可以得到,而不仅是用户运行查询的时候。这种方式,每次查询返回的对象都已经被载入了缓存,一次或多次潜在的数据库事务可以避免。
除此之外,你可以使用查询级别缓存如果你需要缓存的实际的查询结果,而不仅仅是持久化对象。
缓存的实现
缓存是软件中复杂的部分,市场上也提供了相当数量的选择,包括基于开源的和商业的。hibernate提供了以下开源的缓存实现,如下:
* EHCache (org.hibernate.cache.EhCacheProvider)
* OSCache (org.hibernate.cache.OSCacheProvider)
* SwarmCache (org.hibernate.cache.SwarmCacheProvider)
* JBoss TreeCache (org.hibernate.cache.TreeCacheProvider)
不同的缓存提供了在性能,内存使用和配置的可扩展性方面不同的能力
EHCache是一种快速的,轻量级的,容易上手的缓存。它支持只读和读写缓存,基于内存和硬盘的数据储存。但是,它不支持簇。
OSCache是另一个开源的缓存解决方案,它是一个也为jsp页面和任意对象提供缓存功能的大的开发包的一部分。它本身也是一个强大和灵活的开发包,像EHCache一样,也支持只读和读写缓存,基于内存和硬盘的数据储存。它通过JavaGroups或者JMS也提供了对簇的基本支持。
SwarmCache是一种基于簇的解决方案,它本身也基于集群服务实体间通信的通信协议。它提供了只读和没有限制的读写缓存(下一部分将解释这个名词)。这种类型的缓存对那些典型的读操作比写操作多的多的应用程序是适合的。
JBoss TreeCache是一种强大的两重性(同步的或异步的)和事务性的缓存。使用此实现如果你确实需要一种事务能力的缓存架构。
另一种值得提及的缓存实现是商业化的Tangosol Coherence cache。
缓存策略
一旦你选择了你的缓存实现,你需要指定你的缓存策略。以下是四种缓存策略:
只读:这种策略对那种数据被频繁读取但是不更新是有效的,这是到目前为止最简单,表现最好的缓存策略。
读写:读写缓存对假设你的数据需要更新是必要的。但是读写需要比只读缓存花费更多的资源。在非JTA的事务环境中,每一个事务需要完成在Session.close() 或Session.disconnect()被调用的时候。
没有限制的读写:这个策略不能保证2个事务不会同时的修改相同的数据。因此,它可能对那些数据经常被读取但只会偶尔进行写操作的最适合。
事务性:这是个完全事务性的缓存,它可能只能被用在JTA环境中。
对每一个缓存实现来说,支持策略不是唯一的。图一展现了对
缓存实现可供的选择。
文章余下的部分用来展示一个使用EHCache单一的JVM缓存。
缓存配置
为了启用二级缓存,我们需要在hibernate.cfg.xml配置文件中定义hibernate.cache.provider_class属性,如下
<hibernate-configuration> <session-factory> ... <property name="hibernate.cache.provider_class"> org.hibernate.cache.EHCacheProvider </property> ... </session-factory> </hibernate-configuration>
为了在hibernate3中测试的目的,你还要使用hibernate.cache.use_second_level_cache属性,它可以让你启用(和关闭)二级缓存。默认情况下,二级缓存是启用的同时使用EHCache。
一个实际应用
这个例子演示的程序包含四张简单的表:国家列表,机场列表,员工列表,语言列表。每个员工被分配了一个国家,并且能说很多语言。每个国家能有任意数量的机场。
图一展现了UML类图
图二展现了数据库架构
这个例子的源代码(http://assets.devx.com/sourcecode/14239.tgz)包括了以下的SQL脚本,你需要用它创建和实例化数据库。
* src/sql/create.sql: 创建数据库的SQL脚本
* src/sql/init.sql: 测试数据
安装Maven2
在写的时候,Maven2安装目录看来好像缺少了一些jars。为了解决这个问题,在应用程序源码的根目录里找到那些缺少的jars。把它们安装到Maven2的文件里,到应用程序的目录下,执行以下的命令
$ mvn install:install-file -DgroupId=javax.security -DartifactId=jacc
-Dversion=1.0
-Dpackaging=jar -Dfile=jacc-1.0.jar
$ mvn install:install-file -DgroupId=javax.transaction -DartifactId=jta -Dversion=1.0.1B
-Dpackaging=jar -Dfile=jta-1.0.1B.jar
建立一个只读缓存
从简单的开始,下面是country类的hibernate映射。
<hibernate-mapping package="com.wakaleo.articles.caching.businessobjects"> <class name="Country" table="COUNTRY" dynamic-update="true"> <meta attribute="implement-equals">true</meta> <cache usage="read-only"/> <id name="id" type="long" unsaved-value="null" > <column name="cn_id" not-null="true"/> <generator class="increment"/> </id> <property column="cn_code" name="code" type="string"/> <property column="cn_name" name="name" type="string"/> <set name="airports"> <key column="cn_id"/> <one-to-many class="Airport"/> </set> </class> </hibernate-mapping>
假设你要显示国家列表。你可以通过CountryDAO类中一个简单的方法实现,如下
public class CountryDAO { ... public List getCountries() { return SessionManager.currentSession() .createQuery( "from Country as c order by c.name") .list(); } }
因为这个方法经常被调用,所以你要知道它在压力下的行为。写一个简单的单元测试来模拟5次连续的调用。
public void testGetCountries() { CountryDAO dao = new CountryDAO(); for(int i = 1; i <= 5; i++) { Transaction tx = SessionManager.getSession().beginTransaction(); TestTimer timer = new TestTimer("testGetCountries"); List countries = dao.getCountries(); tx.commit(); SessionManager.closeSession(); timer.done(); assertNotNull(countries); assertEquals(countries.size(),229); } }
你能够运行这个测试通过自己喜欢的IDE或者Maven2的命令行(演示程序提供了2个Maven2的工程文件)。这个演示程序通过本地的mysql来测试。当你运行这个测试的时候,应该得到类似以下的一些信息:
$mvn test -Dtest=CountryDAOTest
...
testGetCountries: 521 ms.
testGetCountries: 357 ms.
testGetCountries: 249 ms.
testGetCountries: 257 ms.
testGetCountries: 355 ms.
[surefire] Running com.wakaleo.articles.caching.dao.CountryDAOTest
[surefire] Tests run: 1, Failures: 0, Errors: 0, Time elapsed: 3,504 sec
可以看出每次调用大概花费半秒的时间,对大多数标准来说还是有点迟缓的。国家的列表很可能不是经常的改变,所以这个类可以作为只读缓存一个好的候选。所以加上去
你可以启用二级缓存用以下两种方法的任意一种
1.你可以在*.hbm.xml里启用它,使用cache的属性
<hibernate-mapping package="com.wakaleo.articles.caching.businessobjects"> <class name="Country" table="COUNTRY" dynamic-update="true"> <meta attribute="implement-equals">true</meta> <cache usage="read-only"/> ... </class> </hibernate-mapping>
2.你可以存储所有缓存信息在hibernate.cfg.xml文件中,使用class-cache属性
<hibernate-configuration> <session-factory> ... <property name="hibernate.cache.provider_class"> org.hibernate.cache.EHCacheProvider </property> ... <class-cache class="com.wakaleo.articles.caching.businessobjects.Country" usage="read-only" /> </session-factory> </hibernate-configuration>
下一步,你需要为这个类设置缓存规则,这些规则决定了缓存怎么表现的细节。这个例子的演示是使用EHCache,但是记住每一种缓存实现是不一样的。
EHCache需要一个配置文件(通常叫做ehcache.xml)在类的根目录。EHCache配置文件的详细文档可以看这里(http://ehcache.sourceforge.net/documentation)。基本上,你要为每个需要缓存的类定义规则,以及一个defaultCache在你没有明确指明任何规则给一个类的时候使用。
对第一个例子来说,你可以使用下面简单的EHCache配置文件
<ehcache> <diskStore path="java.io.tmpdir"/> <defaultCache maxElementsInMemory="10000" eternal="false" timeToIdleSeconds="120" timeToLiveSeconds="120" overflowToDisk="true" diskPersistent="false" diskExpiryThreadIntervalSeconds="120" memoryStoreEvictionPolicy="LRU" /> <cache name="com.wakaleo.articles.caching.businessobjects.Country" maxElementsInMemory="300" eternal="true" overflowToDisk="false" /> </ehcache>
这个文件为countries类建立一个基于内存最多300单位的缓存(countries类包含了229个国家)。注意缓存不会过期('eternal=true'属性)。现在通过返回的结果看下缓存的表现
$mvn test -Dtest=CompanyDAOTest
...
testGetCountries: 412 ms.
testGetCountries: 98 ms.
testGetCountries: 92 ms.
testGetCountries: 82 ms.
testGetCountries: 93 ms.
[surefire] Running com.wakaleo.articles.caching.dao.CountryDAOTest
[surefire] Tests run: 1, Failures: 0, Errors: 0, Time elapsed: 2,823 sec
正如你期盼的那样,第一次查询没有改变因为需要加载数据。但是,随后的几次查询就快多了。
后台
在我们继续之前,看下后台发生了什么非常有用。一件事情你需要知道的是hibernate缓存不储存对象实例,代替的是它储存对象“脱水”的形式(hibernate的术语),也就是作为一系列属性值。以下是一个countries缓存例子的内容
{
30 => [bw,Botswana,30],
214 => [uy,Uruguay,214],
158 => [pa,Panama,158],
31 => [by,Belarus,31]
95 => [in,India,95]
...
}
注意每个ID是怎么样映射到拥有属性值的数组的。你可能也注意到了只有主要的属性被储存了,而没有airports属性,这是因为airports属性只是一个关联:对其他持久化对象一系列的引用。
默认情况下,hibernate不缓存关联。而由你决定来缓存哪个关联,哪个关联需要被重载当缓存对象从二级缓存获得的时候。
关联缓存是一个非常强大的功能。下一部分我们将介绍更多的内容。
和关联缓存一起工作
假设你需要显示一个给定的国家的所有的员工(包括员工的名字,使用的语言等)。以下是employee类的hibernate映射
<hibernate-mapping package="com.wakaleo.articles.caching.businessobjects"> <class name="Employee" table="EMPLOYEE" dynamic-update="true"> <meta attribute="implement-equals">true</meta> <id name="id" type="long" unsaved-value="null" > <column name="emp_id" not-null="true"/> <generator class="increment"/> </id> <property column="emp_surname" name="surname" type="string"/> <property column="emp_firstname" name="firstname" type="string"/> <many-to-one name="country" column="cn_id" class="com.wakaleo.articles.caching.businessobjects.Country" not-null="true" /> <!-- Lazy-loading is deactivated to demonstrate caching behavior --> <set name="languages" table="EMPLOYEE_SPEAKS_LANGUAGE" lazy="false"> <key column="emp_id"/> <many-to-many column="lan_id" class="Language"/> </set> </class> </hibernate-mapping>
假设你每次使用employee对象的时候需要得到一个员工会说的语言。强逼hibernate自动载入关联的languages集合,你设置了lazy属性为false()。你还需要一个DAO类来得到所有employee,以下的代码来帮你实现
public class EmployeeDAO { public List getEmployeesByCountry(Country country) { return SessionManager.currentSession() .createQuery( "from Employee as e where e.country = :country " + " order by e.surname, e.firstname") .setParameter("country",country) .list(); } }
下一步,写一些简单的单元测试来看它怎么表现。正如前面的例子一样,你需要知道它被重复调用时候的性能
public class EmployeeDAOTest extends TestCase { CountryDAO countryDao = new CountryDAO(); EmployeeDAO employeeDao = new EmployeeDAO(); /** * Ensure that the Hibernate session is available * to avoid the Hibernate initialisation interfering with * the benchmarks */ protected void setUp() throws Exception { super.setUp(); SessionManager.getSession(); } public void testGetNZEmployees() { TestTimer timer = new TestTimer("testGetNZEmployees"); Transaction tx = SessionManager.getSession().beginTransaction(); Country nz = countryDao.findCountryByCode("nz"); List kiwis = employeeDao.getEmployeesByCountry(nz); tx.commit(); SessionManager.closeSession(); timer.done(); } public void testGetAUEmployees() { TestTimer timer = new TestTimer("testGetAUEmployees"); Transaction tx = SessionManager.getSession().beginTransaction(); Country au = countryDao.findCountryByCode("au"); List aussis = employeeDao.getEmployeesByCountry(au); tx.commit(); SessionManager.closeSession(); timer.done(); } public void testRepeatedGetEmployees() { testGetNZEmployees(); testGetAUEmployees(); testGetNZEmployees(); testGetAUEmployees(); } }
如果你运行上面的代码,你会得到类似以下的一些数据
$mvn test -Dtest=EmployeeDAOTest
...
testGetNZEmployees: 1227 ms.
testGetAUEmployees: 883 ms.
testGetNZEmployees: 907 ms.
testGetAUEmployees: 873 ms.
testGetNZEmployees: 987 ms.
testGetAUEmployees: 916 ms.
[surefire] Running com.wakaleo.articles.caching.dao.EmployeeDAOTest
[surefire] Tests run: 3, Failures: 0, Errors: 0, Time elapsed: 3,684 sec
所以对一个国家载入大约50个员工需要花费大约一秒的时间。这种方法显然太慢了。这是典型的N+1的查询问题。如果你启用SQL日志,你会发现对employee表的一次查询,紧跟着对language表几百次的查询,无论什么时候hibernate从缓存里得到一个employee对象,它都会重载所有关联的language。那怎么提升它的性能呢?第一件要做的事就是对employee启用读写缓存,如下
<hibernate-mapping package="com.wakaleo.articles.caching.businessobjects"> <class name="Employee" table="EMPLOYEE" dynamic-update="true"> <meta attribute="implement-equals">true</meta> <cache usage="read-write"/> ... </class> </hibernate-mapping>
你还应该对language类启用缓存。只读缓存如下
<hibernate-mapping package="com.wakaleo.articles.caching.businessobjects"> <class name="Language" table="SPOKEN_LANGUAGE" dynamic-update="true"> <meta attribute="implement-equals">true</meta> <cache usage="read-only"/> ... </class> </hibernate-mapping>
然后你需要配置缓存的规则通过加入以下的内容到ehcache.xml文件中
<cache name="com.wakaleo.articles.caching.businessobjects.Employee" maxElementsInMemory="5000" eternal="false" overflowToDisk="false" timeToIdleSeconds="300" timeToLiveSeconds="600" /> <cache name="com.wakaleo.articles.caching.businessobjects.Language" maxElementsInMemory="100" eternal="true" overflowToDisk="false" />
但是还是没有解决N+1的查询问题:当你载入一个employee对象的时候大约50次的额外查询还是会执行。这里你就需要在Employee.hbm.xml映射文件里关联的language启用缓存,如下
<hibernate-mapping package="com.wakaleo.articles.caching.businessobjects"> <class name="Employee" table="EMPLOYEE" dynamic-update="true"> <meta attribute="implement-equals">true</meta> <id name="id" type="long" unsaved-value="null" > <column name="emp_id" not-null="true"/> <generator class="increment"/> </id> <property column="emp_surname" name="surname" type="string"/> <property column="emp_firstname" name="firstname" type="string"/> <many-to-one name="country" column="cn_id" class="com.wakaleo.articles.caching.businessobjects.Country" not-null="true" /> <!-- Lazy-loading is deactivated to demonstrate caching behavior --> <set name="languages" table="EMPLOYEE_SPEAKS_LANGUAGE" lazy="false"> <cache usage="read-write"/> <key column="emp_id"/> <many-to-many column="lan_id" class="Language"/> </set> </class> </hibernate-mapping>
通过这个配置,你就能得到几近最优的性能
$mvn test -Dtest=EmployeeDAOTest
...
testGetNZEmployees: 1477 ms.
testGetAUEmployees: 940 ms.
testGetNZEmployees: 65 ms.
testGetAUEmployees: 65 ms.
testGetNZEmployees: 76 ms.
testGetAUEmployees: 52 ms.
[surefire] Running com.wakaleo.articles.caching.dao.EmployeeDAOTest
[surefire] Tests run: 3, Failures: 0, Errors: 0, Time elapsed: 0,228 sec
查询缓存
在确定的情况下,缓存一次查询的正确结果是非常有用的,不仅是只是个确定的对象。例如,getCountries()方法在每次调用的时候或许会返回相同的国家列表。所以,除了缓存country类之外,你也应该缓存查询结果本身。
为了实现这个目标,你需要在hibernate.cfg.xml文件中设置hibernate.cache.use_query_cache属性为true,如下
<property name="hibernate.cache.use_query_cache">true</property>
然后需要在对任何查询缓存的时候使用setCacheable()方法,如下
public class CountryDAO { public List getCountries() { return SessionManager.currentSession() .createQuery("from Country as c order by c.name") .setCacheable(true) .list(); } }
但是,它不能预知到其他程序对数据库任何的改变。所以你不应该使用任何二级缓存(或为类和集合的缓存设置简短的过期时间)如果你的数据总是要保证最新的状态。
正确的使用hibernate缓存
缓存是一种强大的技术,hibernate提供了一种强大的,灵活的,不显眼的方式来实现它。即使对很多简单的例子来说默认的设置能使实际的性能得到提升。但是,像很多强大工具一样,hibernate还是需要一些思考和微调来得到最优的结果,而缓存像其他的优化技术一样,应该使用一种可扩展,测试驱动的方法所实现。当正确使用的时候,少量的缓存实现,能最大程度的提高你的程序运行。
评论
21 楼
dean_liu
2010-01-28
如果没有缓存“查询(Query)”,list方法是不会使用缓存的吧
20 楼
jiangpingzhan
2008-10-09
麻烦各位用真实的案例来说明自己的观点 ok ? 谢绝漫骂
19 楼
icewubin
2008-10-08
引用
楼主写的很全面、很实用,后边跟帖的人大部分对Hibernate的缓存一知半解,乱叫的。
请你自己写几个二级缓存的应用在自己的项目中,并作测试,你就知道怎么回事了。
18 楼
wzbwambition
2008-10-08
二级缓存的数据是有选择性的,只有不被经常修改的数据才会配置二级缓存。
17 楼
wzbwambition
2008-10-08
楼主写的很全面、很实用,后边跟帖的人大部分对Hibernate的缓存一知半解,乱叫的。
16 楼
raymond2006k
2008-10-08
不要拿“内存珍贵”来攻击Hibernate二级缓存了。 楼主文章已经说的很明白了, Hibernate 只是对数据进行缓存控制,这一点很强大了;数据的实际存储是由不同的第三方 Cache 方案来完成的。
存储方案有同JVM内存缓存,和分布式缓存两种架构, EHCache内存缓存只是为了举例说明而已。 具体项目中,如果数据量大,肯定不会用内存缓存; 而要用 memcache,ehcache(分布式版本), jboss cache, coherence 等。
存储方案有同JVM内存缓存,和分布式缓存两种架构, EHCache内存缓存只是为了举例说明而已。 具体项目中,如果数据量大,肯定不会用内存缓存; 而要用 memcache,ehcache(分布式版本), jboss cache, coherence 等。
15 楼
headof
2008-10-07
allenny 写道
jkfzero 写道
不久前因为用了内存缓存被狂BS,说服务器的内存是极为珍贵的资源……貌似数据库不是珍贵的资源?!
应该说是得看什么项目。比较倾向自己写的CACHE
14 楼
SSailYang
2008-10-07
原文:
译文:
use it poorly as a result 结果并没有充分地使用它(二级缓存)
看来楼主要好好学英文呀,不过精神可嘉。
引用
Newer Hibernate developers sometimes don't understand Hibernate caching and use it poorly as a result.
译文:
引用
新的hibernate开发人员有时并不知道hibernate的缓存而只是简单的把它作为一种结果
use it poorly as a result 结果并没有充分地使用它(二级缓存)
看来楼主要好好学英文呀,不过精神可嘉。
13 楼
guoshiguan
2008-10-06
iampurse 写道
这.. 那如果缓存里的东西跟数据库已经不一样勒捏?
而且,用这种缓存,内存很快就会爆炸的吧 ?
比方10亿条记录里有1亿条符合条件~
个人觉得优化数据库查询应该从数据库本身入手,
提供索引等等的优化方案。
没人让你把10亿条记录让你都缓存起来吧,
12 楼
icewubin
2008-10-06
引用
正确的使用hibernate缓存
问题就在这,当你在一个项目中正确使用二级缓存以后,会发现这个时间早就能实现自己的根据业务需要特殊优化的缓存。
自己针对业务设计的缓存效率要远远超过Hibernate自己的二级缓存机制,因为Hibernate是一个通用机制,为了这个通用特性就得牺牲不少东西,包括内存使用空间是时间成本。
11 楼
ksysteven
2008-10-05
讲得非常好
10 楼
iampurse
2008-10-05
这.. 那如果缓存里的东西跟数据库已经不一样勒捏?
而且,用这种缓存,内存很快就会爆炸的吧 ?
比方10亿条记录里有1亿条符合条件~
个人觉得优化数据库查询应该从数据库本身入手,
提供索引等等的优化方案。
而且,用这种缓存,内存很快就会爆炸的吧 ?
比方10亿条记录里有1亿条符合条件~
个人觉得优化数据库查询应该从数据库本身入手,
提供索引等等的优化方案。
9 楼
glacier3
2008-10-04
世界级的设计是不会使用那些乱七八糟的框架的,会根据需求来设计符合本应用的的方案
8 楼
glacier3
2008-10-04
这样的查询语句应该没有使用到2级缓存吧!?
速度照样会有问题。
速度照样会有问题。
7 楼
zpl3001
2008-10-04
neptune 写道
hibernate的二级缓存,我有一个问题,如果一个类的实例已被cache,则下次调用get方法时,其是先得到连接,再从cache中获取实例呢?还是先判断cache中是否有实例,如果不存在,得到连接从数据库中读取?
http://zem.iteye.com/blog/138683,这里有篇文章你可以参考下
6 楼
neptune
2008-10-04
hibernate的二级缓存,我有一个问题,如果一个类的实例已被cache,则下次调用get方法时,其是先得到连接,再从cache中获取实例呢?还是先判断cache中是否有实例,如果不存在,得到连接从数据库中读取?
5 楼
allenny
2008-10-04
jkfzero 写道
不久前因为用了内存缓存被狂BS,说服务器的内存是极为珍贵的资源……
貌似数据库不是珍贵的资源?!
4 楼
jkfzero
2008-10-03
不久前因为用了内存缓存被狂BS,说服务器的内存是极为珍贵的资源……
3 楼
gstripe
2008-10-03
JAVA认为内存都不需要钱
只要不溢出就万事OK了。
只要不溢出就万事OK了。
2 楼
gstripe
2008-10-03
JAVA认为内存都不需要钱
只要不溢出就万事OK了。
只要不溢出就万事OK了。
发表评论
-
BPEL(1)
2009-02-03 17:37 2648随着Web Servcie技术日益成熟和流行,许多企业的很多部 ... -
常见设计模式的简介(一)
2008-09-23 22:00 0本文将主要介绍单态模 ... -
getContextPath、getServletPath、getRequestURI的区别
2008-09-09 16:29 50082工程图见附件: 假定你的web application 名称为 ... -
分页技术的原理及其实现
2008-07-24 15:40 2755分页问题是一个非常普 ... -
web会话状态维持
2008-07-23 22:01 1659http://blog.csdn.net/treeroot/a ...
相关推荐
在实际开发中,可以结合二级缓存(如 Ehcache 或 Redis)进一步优化数据访问效率,实现更高效的缓存策略。 总结,Hibernate的一级缓存是提升应用程序性能的重要工具。它在Session内部提供了一种内存级别的缓存机制...
Hibernate提供了两层缓存机制:第一级缓存和第二级缓存。 - **第一级缓存**:也称为Session缓存,是Hibernate内置并自动管理的缓存。每当Session开启,就会创建一个与之关联的第一级缓存。此缓存用于存储当前...
- **Hibernate二级缓存**:通过第三方缓存提供者如EHCache或OSCache实现,提高Web应用性能。 2. **查询缓存**:Hibernate查询缓存通过缓存查询结果,避免重复执行相同的查询。 #### 八、页面缓存与动态页面静态化 ...
本文将深入探讨Hibernate中的缓存机制,包括一级缓存、二级缓存以及查询缓存,旨在帮助开发者更好地理解和利用这些功能来优化应用性能。 首先,我们要了解缓存的基本概念。缓存是一种存储技术,它位于应用程序和...
Hibernate是一个开源的对象关系映射(ORM)框架,它允许Java开发者在Java应用程序中使用数据库时,不必编写大量的SQL代码,而是通过对象的方式来操作数据。这个压缩包文件包含了Hibernate开发所需的大部分核心库,...
**hibernate-redis** 是一个Hibernate的第二级缓存插件,它实现了Hibernate的CacheProvider接口,将数据缓存在Redis中。通过这个插件,开发者可以将经常访问但不经常改变的数据存储在Redis中,从而减少对数据库的...
1. **缓存策略配置**:理解并合理配置第一级和第二级缓存,可以显著提高应用程序的响应速度,但需注意缓存一致性问题。 2. **懒加载与Eager加载**:根据业务场景选择合适的加载策略,避免N+1查询问题,提高性能。 ...
通过本文对Hibernate的深入剖析,我们不仅了解了它的基本原理和核心概念,还学习了如何有效地使用其提供的各种API来构建高性能的应用程序。Hibernate作为一种强大的ORM框架,在简化Java应用开发的同时,也带来了诸如...
### Hibernate性能优化方案详解 #### 一、引言 Hibernate作为Java领域中广泛使用的对象关系映射(ORM)...通过对这些方面的综合考量和优化,可以极大地提升Hibernate应用程序的性能表现,从而更好地满足业务需求。
- **缓存机制**:内置一级缓存,可配置二级缓存,提高了数据访问效率。 #### 三、整合框架的可行性分析 将 Struts、Spring 和 Hibernate 进行整合,可以构建出一个高度模块化、灵活且高效的 J2EE 架构。这种整合的...
2. 第二级缓存:Hibernate3.6.8增强了第二级缓存的性能和可配置性,使得数据可以在多个Session之间共享,提高应用的响应速度。 3. 改进的性能:优化了查询处理和对象状态管理,减少了内存占用并提高了整体性能。 4...
- **Hibernate二级缓存**:SessionFactory级别,跨Session,可配置为进程内或集群间共享,缓存查询结果和集合数据。 3. **分布式系统中的缓存(Memcached、Xmemcached)** - **分布式缓存**:在分布式环境中,...
为了提高性能,Hibernate提供了二级缓存机制,可以缓存查询结果,避免重复的数据库访问。同时,Hibernate还支持缓存的区域划分,便于控制不同数据的缓存策略。 ### Hibernate的高级特性 #### 批量操作与性能优化 ...
- **缓存机制**:为了提高性能,Hibernate支持一级缓存和二级缓存,能够有效地减少数据库访问次数。 3. **Spring与Hibernate集成的最佳实践** - **整合配置**:通过Spring配置文件或注解来管理和配置Hibernate...
- **ORM(Hibernate,Toplink)缓存**:ORM框架通常内置了一级缓存和二级缓存机制,以减少对数据库的访问次数。 - **数据库层的缓存**:在数据库级别进行缓存,如使用MySQL的Query Cache。 - **业务对象的缓存**:...
- **Hibernate**:作为默认的二级缓存提供者。 - **Spring**:结合Spring框架,实现数据的缓存管理。 - **其他开源系统**:如Cocoon等。 #### 三、Ehcache的主要特性 1. **快速**:Ehcache采用高效的数据结构和...
EHCache 可以作为 Hibernate 的二级缓存插件,提高基于 Hibernate 构建的应用程序性能。 - **配置 Hibernate**: 在 `hibernate.cfg.xml` 文件中配置: ```xml <property name="hibernate.cache.provider_...