`

Hibernate二级缓存 ---- 最佳实践

 
阅读更多

者:赵磊

博客:http://elf8848.iteye.com

 

    2010年11月7号,立冬,星期天。北京外面风好大,躲在家里整理一下这篇文章,发出来与大家分享,对大家有帮助是我最高兴的事儿。

 

    不要想当然的认为使用了Hibernate的二级缓存就一定能够提高应用程序的性能,仅仅在你能够驾驭它,并且条件适合的情况下才是这样的。
    使用hibernate的二级缓存限制还是比较多的。在不了解原理的情况下,可能缓存中的数据频繁的被清空性能下降,可能会有1+N的问题,在批量insert,update数据时二级缓存会占用大量内存,就算不溢出也会花费长时间来GC,不了解缓存的锁可能会出现死锁、脏数据。

 

一、缓存应用的场景:
1、对于新闻,论坛,博客等互联网应用适合在前端做缓存,比如url做为key来缓存整个页面的内容。一条新闻a被如前所述的缓存起来了,在网站并发访问量大时,会大大提高网站的吞吐能力。好了现再须要编辑这条新闻,如何同步更新缓存呢?须要立即同步更新缓存吗?不须要,互联网应用允许用户在5-10分钟之后再看到更新之后的新闻,这是可以接受的。没有较高的时效性,允许延迟。这样我们设定缓存对象的最大生命时间为10分钟,一个被缓存的对象存活时间超过10分钟就被清理,当新的访问请求到来时,再从数据库中加载他,再次被缓存10分钟。Hibernate二级缓存不适合这个场景,这个场景对缓存的锁、事务没有要求,对高并发,高数据量有要求。
    总结一下:被缓存的对象没有较高的时效性,允许对象更新后延迟(10分钟内)展示,允许(10分钟内)的数据不一致。

2、对于企业应用,要保证数据的一致性是第一位的,即使数据被修改,最终用户看到的数据与数据库中的数据要时时一致。适合在应用程序持久层上做缓存,Hibernate二级缓存就适合这个场景。
    总结一下:这个场景对缓存的锁,事务要求是第一的。对高并发高数据量的要求是第二的,通过锁保证数据的一致性。Hibernate对数据库是独占的,修改给数据库的操作都通过他.

 

二、频繁更新的数据要不要被缓存:
网上有人说频繁更新的数据不适合使用缓存。这样说是不全面的,因为他少说了前提条件。

 

数据一致性:本文章的数据一致性是指缓存中的数据与数据库中的数据就保持一致,严格的一致。决对没有脏数据。

 

当你须要数据一致性,而又不能保存数据一致性时,频繁更新的数据就不可以被缓存。不缓存直接操作数据库,就一致了,没有不一致的问题了。

      你的Hibernate对数据库不是独占的,有其它程序来修改数据库中的记录,这时Hibernate是不知道的,也会发生数据不一致.

      当使用url或sql语句做为KEY来缓存时,一句select 语句查出n个对象,无法在缓存中精准的找到被修改的某一个对象,当修改一个对象时就不能在缓存中精准的找到他,为了保证数据一致性,就要清除缓存中所有的同类对象,使下次查询时无法命中缓存。而不清除,很有可能发生数据不一致。这相当于Hibernate的查询缓存。这时个不要使用缓存。

     
当你须要数据一致性,而又能保存数据一致性时,频繁更新的数据是可以被缓存的。这里我们使用缓存的“锁”机制来保证,你使用的Hibernate第三方缓存要支持“锁”,就是read-write模式。这是重点啊。第三方缓存锁的实现方法不同性能也不同,锁是缓存性能下降第一原因,一定要使用高性能的锁,这就要了解多款Hibernate第三方缓存.

      因为经常被更新修改的对象,一定也更加经常的被查询,需要缓存他来提高应用程序的性能。如果执行修改sql时,同时锁住缓存中的这个对象并更新他,之后解锁是最理想的。Hibernate的二级缓存策略,是针对于ID查询的缓存策略,所以可以做到精准的找到缓存中的目标,加之“锁”的帮助,可实现数据一致性。

但限制也有比如使用HQL时就不能精准的找到缓存中的目标,只好清除缓存中所有的同类对象来保证数据的一致性(缓存中没数据就没一致性问题了)。


三、Hibernate二级缓存中的对象什么时候会被清理:
在read-write模式下:
我们有一个Order对象,是一个实体对象,对应数据库中order表中的一条记录,经过查询已有n个Order对象被放入二级缓存中。现在我们要修改order表中任意任x条记录,执行以下HQL:

template.bulkUpdate("update Order set owner = ? where id in (?,?,?)");


 

 这时Hibernate会直接将二级缓存中的n个Order对象清除掉。 天啊,居然不是你想像的修改谁就同步更新二级缓存中的谁,而是清除了二级缓存中全部的Order类型的对象。为什么?这一切是为了保证“数据一致性”。你执行了HQL修改了order表中的x条记录,这x条是哪几条?如果sql是子查询:update Order set owner =? where id in(select id from *** ),谁知道你修改了order表中的哪几条记录,你自己都不知道,Hibernate更不知道了。所以为了保证二级缓存中的数据与order表中的数据一致,只能清除了二级缓存中全部的Order类型的对象。二级缓存频繁的载入与清除,这样缓存命中率就会下降。

试验:
看到这里后,我很担心,这样命中率下降后,没有起到缓存的作用。今天特意做一个实验,看看被缓存的对象在被修改后会怎样。

环境:Hibernate3.4 , OsCache(usage="read-write"),JUnit
缓存状态:Hibernate二级缓存中已缓存了5个Order对象。
测试结果:
1 使用saveOrUpdate()方法更新一个实体对象a时,新的a对象被put到二级缓存中,同时写入数据库,二级缓存中的其它4个Order对象没有变化。
这时再查询这5个Order对象中的任意,是可以命中二级缓存的。
2 使用HQL "update Order set name = ? where id =?" 方法更新一个实体对象a时,所有Order对象被从二级缓存中清除,同时a对象被写入数据库。
这时再查询这5个Order对象中的任意,无法命中二级缓存,会去查数据库,查出来的对象又put进二级缓存。

Hibernate的二级缓存策略,是以ID做为key 的缓存策略,在删除、更新、增加数据的时候,同时更新缓存。
对于条件查询,条件修改,条件删除(一般是执行HQL)则起不到缓存的作用。条件修改,条件删除时(一般是执行HQL)会清空所有在缓存中的同类对象。
为此,Hibernate提供了针对条件查询的Query Cache,其实它并不好用。

关于是否命中,是使用Statistics类监测的(通过SessionFactory的getStatistics()方法得到)。

总结一下:如果你打算开启hibernate的二级缓存,在修改与删除时,就要使用session.update(),session.delete()方法按ID一条一条的操作,这样对二级缓存是最优的。
    但循环中使用sesion.update(),session.delete()方法,会产生多条sql语句,原本使用一条HQL完成的工作,现在要执行多条,你担心Hibernate与数据库服务器的网络通信次数吗?其实这多条sql是使用JDBC的批处理一次发送到数据库服务器的,所以你不用担心。现在到了数据库服务器端,我们以oracle为例,oracle要执行多条sql,就要进行多次的“分析sql语句的正确性,并解析成oracle的原子操作,并制定执行计划”,你担心这“多次”分析会给oracle带来性能的影响吗?不用担心,请使用oracle的绑定参数,就是Hibernate中的?代替参数。


四、Hibernate二级缓存的并发策略你了解吗:

1 只读缓存 read only
不须要锁与事务,因为缓存自数据从数据库加载后就不会改变。

    如果数据是只读的,例如引用数据,那么总是使用“read-only”策略,因为它是最简单、最高效的策略,也是集群安全的策略。是性能第一的策略 。

2 读写缓存 read write
对缓存的更新发生在数据库事务完成后。缓存需要支持锁。
在一个事务中更新数据库,在这个事务成功完成后更新缓存,并释放锁。
锁只是一种特定的缓存值失效表述方式,在它获得新数据库值前阻止其他事务读写缓存。那些事务会转而直接读取数据库。
缓存必须支持锁,事务支持则不是必须的。如果缓存是一个集群,“更新缓存”的调用会将新值推送给所有副本,这通常被称为“推(push)”更新策略。

    如果你的数据是又读又写的,那么使用“read-write”策略。这通常是性能第三的策略,因为它要求有缓存锁,缓存集群中使用重量级的“推”更新策略。

3 非严格读写缓存 nonstrict read write
在一个事务中更新数据库,在这个事务完成前就清除缓存,为了安全起见,无论事务成功与否,在事务完成后再次清除缓存。
既不需要支持缓存锁,也不需要支持事务。如果是缓存集群,“清除缓存”调用会让所有副本都失效,这通常被称为“拉(pull)”更新策略。

    如果你的数据读很多或者很少有并发缓存访问和更新,那么可以使用“nonstrict-read-write”策略。感谢它的轻量级“拉”更新策略,它通常是性能第二好的策略。

4 事务缓存 transactional (一定要在JTA环境中)
对缓存和数据库的更新被包装在同一个JTA事务中,这样缓存与数据库总是保持同步的。数据库和缓存都必须支持JTA。

    除非你真的想将缓存更新和数据库更新放在一个JTA事务里,否则不要使用“transactional”策略,因为JTA需要漫长的两阶段提交处理,这导致它基本是性能最差的策略。


五、缓存锁的性能也要了解,知道加了锁后性能会下降:
    为了保证数据的安全性,不发生脏数据,各个缓存通常使用锁来保证
在本地方式运行时,缓存最大的开销就是使用锁来在保证共享数据完整性。
在集群环境中,RPC调用,锁,是性能上大开销。

 

下面以JBoss Cache为例说一说锁:
JBoss Cache1.* 和 2.* 时代,提供乐观锁,悲观锁,但是性能不高。
JBoss Cache3.0 MVCC锁方案性能很高。

悲观锁:这些锁的隔离级别和数据库实施的隔离级别相同,这种方案简单而且健壮,允许多用户同时读取数据。读操作阻塞写操作,悲观锁的读写是互斥的,无法同时进行的,写的性能不好。

乐观锁:这个方式则牵涉到数据版本,可以获得高度并发性。那些请求读取数据的用户不会因为并发数据库写入操作而受到阻塞。而且,乐观锁定方式还可以避免悲观锁定中有可能发生的死锁。但它仍然有两个主要的缺点:一是性能问题。因为不断的将结点的状态拷贝到每个并发线程所造成的内存和 CPU 开销是不容忽略的。二是尽管并发时允许了写操作,但是一旦发现数据的版本不对,事务提交时不可避免的还是会失败。也就是说,此时写事务虽然可以不受限制的进行大量处理和写操作,但是这样在事务结束的时候容易出现提交失败。

多版本并发控制(MVCC):在数据访问速度上较之前者也胜出百倍。MVCC 提供了非阻塞 (non-blocking) 读操作 ( 它并不会去阻塞 wirter threads) ,在避免死锁的同时也提供了更高级的并发机制。更棒的是,我们的 MVCC 实现甚至可以对 reader threads 完全不采用任何锁 ( 对于像缓存这样频繁读取的系统来说,意义太大了 ) ,

 

六、批量处理时请不要使用二级缓存
当你执行大量的 添加与修改时,并且这个实体对象被配置为启用二级缓存,你考虑过二级缓存会怎么样吗?请看下面代码:

Session session = sessionFactory.openSession(); Transaction tx = session.beginTransaction(); for ( int i=0; i<100000; i++ ) { Customer customer = new Customer(.....); //如果你的 hibernate.cache.use_second_level_cache 是 true, 请在会话级别上关闭他 //向(任何一级)缓存中加载大量数据通常也意味着它们很快会被清除出去,这会增加GC开销。 session.setCacheMode(CacheMode.IGNORE); session.save(customer); if ( i % 50 == 0 ) { //将本批插入的对象立即写入数据库并释放内存 session.flush(); session.clear(); } } tx.commit(); session.close();


 

 

 

 

 

批处理通常不需要数据缓存,否则你会将内存耗尽并大量增加GC开销。如果内存有限,那这种情况会很明显。


七、了解几种优秀缓存方案:
1、Memcached
分布式缓存系统,memcached 要求set的对象必须是可序列化对象,jboss cache等java obect cache是没有这个说法的,这是本质的不同的,但是他可以在网络上用,所以必须序列化也可理解。
独立服务器+java 客户端。
Memcached java 客户端有:
memcache-client-forjava,
XMemcached,
spymemcached,
memcache-client-forjava
参考文章:
XMemcached——一个新的开源Java memcached客户端
缓存系统MemCached的Java客户端优化历程

2、JBOSS CACHE
JBoss Cache是非常优秀的,前面介绍锁的时候已说过了。
参考文章:
深入理解JBoss Cache3.0——Naga

JBoss Cache分布式缓存:Manik Surtani访谈

3、EhCache
Ehcache 2.1起提供了针对Hibernate的JTA支持。
参考:
Ehcache 2.0:后写式缓存和JTA支持

4、Infinispan
开源数据网格平台 ,是新东东。
参考:
开源数据网格平台Infinispan访谈

分享到:
评论

相关推荐

    hibernate一级缓存、二级缓存和查询缓存

    **hibernate一级缓存、二级缓存和查询缓存** 在Java的持久化框架Hibernate中,缓存机制是提高应用程序性能的关键要素。缓存能够减少数据库的访问次数,提高数据读取速度,并且在一定程度上降低了系统的负载。本文将...

    Hibernate4(关系映射-事务-原理-性能和二级缓存-最佳实践)

    Hibernate 4是该框架的一个版本,它涵盖了关系映射、事务处理、原理、性能优化以及二级缓存的使用与最佳实践。以下知识点详细解释了这些关键概念。 1. 关系映射:在Hibernate框架中,关系映射是指对象与数据库表...

    Hibernate 二级缓存 总结整理

    **Hibernate 二级缓存总结整理** 在Java的持久化框架中,Hibernate是一个广泛使用的ORM(对象关系映射)工具,它极大地简化了数据库操作。在处理大数据量或高并发的场景下,为了提高性能和减少数据库负载,...

    Hibernate一级缓存、二级缓存以及查询缓存实例

    本文将深入探讨Hibernate的一级缓存、二级缓存以及查询缓存,通过具体的实例来阐述它们的工作原理和使用方法。 首先,我们从一级缓存开始。一级缓存是Hibernate默认提供的缓存,它是每个Session级别的,也被称为...

    hibernate二级缓存 SSH

    在这个项目中,"hibernate二级缓存 SSH" 的标题表明我们将探讨如何在SSH框架中实现Hibernate的二级缓存功能。Hibernate二级缓存是一个优化策略,它能提高数据访问性能,减少对数据库的直接访问。 首先,Struts2作为...

    hibernate二级缓存jar包

    Ehcache提供了本地缓存、分布式缓存以及缓存的持久化等功能,这使得它成为Hibernate二级缓存的理想选择。在提供的压缩包中,包含了Ehcache的两个不同版本:1.2.3和3.0.2。 1.2.3版的Ehcache是较早的一个稳定版本,...

    hibernate二级缓存

    **hibernate二级缓存详解** 在Java的持久化框架Hibernate中,二级缓存是一个重要的性能优化手段。它提供了一种存储数据的方式,使得在多次请求相同数据时,可以避免频繁地与数据库交互,从而提高应用程序的运行效率...

    Hibernate_二级缓存 实验心得,手册

    通过本次实验,不仅深入了解了Hibernate二级缓存的基本概念和工作原理,还掌握了如何通过配置来优化二级缓存的使用。实践证明,合理的缓存策略能够显著提升系统的性能表现。在实际开发过程中,还需要结合具体的应用...

    Spring4+Hibernate4二级缓存实例源码

    通过这个"Spring4+Hibernate4二级缓存实例源码",你可以学习到如何在实际项目中结合Spring和Hibernate实现二级缓存,提高应用的运行效率。同时,深入理解缓存的工作原理和最佳实践,对于优化系统的性能和架构有着...

    hibernate-release-4.3.10.Final.zip

    - **Session**:是操作数据库的入口,提供了一种基于对象的操作方式,支持CRUD操作,并具有事务管理和二级缓存功能。 4. **映射机制**: - **XML 映射文件**:如hibernate.hbm.xml,定义了Java类与数据库表之间的...

    Hibernate4二级缓存Ehcache案例

    在这个“Hibernate4二级缓存Ehcache案例”中,我们将深入探讨如何利用Ehcache作为Hibernate的二级缓存提供商,以提升应用性能。 首先,我们需要了解什么是二级缓存。一级缓存是Hibernate Session级别的缓存,每个...

    hibernate-release-5.3.2.Final

    1. 性能优化:5.3.2.Final版本在性能方面进行了大量优化,包括查询缓存、二级缓存策略以及连接池管理等,提高了数据处理速度和资源利用效率。 2. JPA 2.2支持:该版本全面支持Java Persistence API 2.2规范,引入了...

    hibernate二级缓

    **hibernate二级缓存详解** 在Java的持久化框架Hibernate中,二级缓存是一个重要的性能优化手段。它能够显著提升应用的响应速度,通过存储经常访问的数据来减少数据库的I/O操作。本文将深入探讨Hibernate的二级缓存...

    Hibernate4(事务,原理,性能和二级缓存,最佳实践).pdf

    - **使用二级缓存**:Hibernate 提供了一级缓存(默认开启)和二级缓存。合理利用二级缓存可以极大地提高应用性能。 - **优化查询**:编写高效的 HQL 或 JPQL 查询,避免不必要的 N+1 问题。 - **懒加载**:对于一对...

    hibernate-distribution-3.5.0-Final-dist文档

    6. **第二级缓存**: Hibernate支持二级缓存,可以提高数据读取速度,尤其是在多线程环境中。 **配置** - **hibernate.cfg.xml**: 这是Hibernate的主配置文件,包含了数据库连接信息、缓存设置、方言选择等核心参数...

    hibernate-release-5.0.0.Final(1).zip

    - 利用缓存机制提高数据访问速度,如一级缓存(Session级)和二级缓存(SessionFactory级)。 五、最佳实践 1. 合理设计实体类:遵循单一职责原则,避免实体类过于庞大,提高代码可维护性。 2. 选择合适的映射策略...

    Hibernate性能优化:一级缓存

    同时,理解一级缓存的原理也有助于我们理解二级缓存和其他缓存解决方案,如Ehcache和Infinispan,以及它们在复杂分布式系统中的应用。 总之,Hibernate的一级缓存是提高应用程序性能的关键因素之一。理解其工作原理...

    hibernate-release-4.3.0.Beta2 lib

    - `ehcache.jar`: EhCache 是 Hibernate 默认的二级缓存提供商,提高数据访问效率。 4. **使用方法** 配置 Hibernate,首先需要在配置文件(通常是 `hibernate.cfg.xml`)中指定数据库连接信息,然后创建 ...

    hibernate-level2-cache:在Hibernate 2级缓存上进行测试

    一、Hibernate二级缓存简介 Hibernate的二级缓存是进程级缓存,它存储了同一个应用程序的所有会话中的对象,不同于一级缓存(Session级别的缓存),二级缓存可以被多个会话共享。二级缓存由第三方插件如Ehcache、...

    hibernate-release-5.2.10

    Hibernate提供了一级缓存(Session级别)和二级缓存(SessionFactory级别)。一级缓存默认开启,二级缓存可配置插件如EhCache实现,提高数据访问效率。 八、延迟加载(Lazy Loading) Hibernate支持懒加载策略,...

Global site tag (gtag.js) - Google Analytics