通过二级缓存来加快你的hibernate应用程序
由于本人也是边翻边学,有什么翻译不当的地方在所难免,欢迎大家批评指正
原文题目: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属性,如下
图一展现了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映射。
2.你可以存储所有缓存信息在hibernate.cfg.xml文件中,使用class-cache属性
下一步,写一些简单的单元测试来看它怎么表现。正如前面的例子一样,你需要知道它被重复调用时候的性能
你还应该对language类启用缓存。只读缓存如下
然后你需要配置缓存的规则通过加入以下的内容到ehcache.xml文件中
5 楼 allenny 2008-10-04 jkfzero 写道
不久前因为用了内存缓存被狂BS,说服务器的内存是极为珍贵的资源……
貌似数据库不是珍贵的资源?! 6 楼 neptune 2008-10-04 hibernate的二级缓存,我有一个问题,如果一个类的实例已被cache,则下次调用get方法时,其是先得到连接,再从cache中获取实例呢?还是先判断cache中是否有实例,如果不存在,得到连接从数据库中读取? 7 楼 zpl3001 2008-10-04 neptune 写道
hibernate的二级缓存,我有一个问题,如果一个类的实例已被cache,则下次调用get方法时,其是先得到连接,再从cache中获取实例呢?还是先判断cache中是否有实例,如果不存在,得到连接从数据库中读取?
http://zem.iteye.com/blog/138683,这里有篇文章你可以参考下 8 楼 glacier3 2008-10-04 这样的查询语句应该没有使用到2级缓存吧!?
速度照样会有问题。 9 楼 glacier3 2008-10-04 世界级的设计是不会使用那些乱七八糟的框架的,会根据需求来设计符合本应用的的方案 10 楼 iampurse 2008-10-05 这.. 那如果缓存里的东西跟数据库已经不一样勒捏?
而且,用这种缓存,内存很快就会爆炸的吧 ?
比方10亿条记录里有1亿条符合条件~
个人觉得优化数据库查询应该从数据库本身入手,
提供索引等等的优化方案。 11 楼 ksysteven 2008-10-05 讲得非常好 12 楼 icewubin 2008-10-06 引用正确的使用hibernate缓存
问题就在这,当你在一个项目中正确使用二级缓存以后,会发现这个时间早就能实现自己的根据业务需要特殊优化的缓存。
自己针对业务设计的缓存效率要远远超过Hibernate自己的二级缓存机制,因为Hibernate是一个通用机制,为了这个通用特性就得牺牲不少东西,包括内存使用空间是时间成本。 13 楼 guoshiguan 2008-10-06 iampurse 写道
这.. 那如果缓存里的东西跟数据库已经不一样勒捏?
而且,用这种缓存,内存很快就会爆炸的吧 ?
比方10亿条记录里有1亿条符合条件~
个人觉得优化数据库查询应该从数据库本身入手,
提供索引等等的优化方案。
没人让你把10亿条记录让你都缓存起来吧, 14 楼 SSailYang 2008-10-07 原文:
引用Newer Hibernate developers sometimes don't understand Hibernate caching and use it poorly as a result.
译文:
引用新的hibernate开发人员有时并不知道hibernate的缓存而只是简单的把它作为一种结果
use it poorly as a result 结果并没有充分地使用它(二级缓存)
看来楼主要好好学英文呀,不过精神可嘉。 15 楼 headof 2008-10-07 allenny 写道
jkfzero 写道
不久前因为用了内存缓存被狂BS,说服务器的内存是极为珍贵的资源……貌似数据库不是珍贵的资源?!
应该说是得看什么项目。比较倾向自己写的CACHE 16 楼 raymond2006k 2008-10-08 不要拿“内存珍贵”来攻击Hibernate二级缓存了。 楼主文章已经说的很明白了, Hibernate 只是对数据进行缓存控制,这一点很强大了;数据的实际存储是由不同的第三方 Cache 方案来完成的。
存储方案有同JVM内存缓存,和分布式缓存两种架构, EHCache内存缓存只是为了举例说明而已。 具体项目中,如果数据量大,肯定不会用内存缓存; 而要用 memcache,ehcache(分布式版本), jboss cache, coherence 等。
17 楼 wzbwambition 2008-10-08 楼主写的很全面、很实用,后边跟帖的人大部分对Hibernate的缓存一知半解,乱叫的。 18 楼 wzbwambition 2008-10-08 二级缓存的数据是有选择性的,只有不被经常修改的数据才会配置二级缓存。 19 楼 icewubin 2008-10-08 引用楼主写的很全面、很实用,后边跟帖的人大部分对Hibernate的缓存一知半解,乱叫的。
请你自己写几个二级缓存的应用在自己的项目中,并作测试,你就知道怎么回事了。 20 楼 jiangpingzhan 2008-10-09 麻烦各位用真实的案例来说明自己的观点 ok ? 谢绝漫骂 21 楼 dean_liu 2010-01-28 如果没有缓存“查询(Query)”,list方法是不会使用缓存的吧