2010年11月7号,立冬,星期天。北京外面风好大,躲在家里整理一下这篇文章,发出来与大家分享,对大家有帮助是我最高兴的事儿。
不要想当然的认为使用了Hibernate的二级缓存就一定能够提高应用程序的性能,仅仅在你能够驾驭它,并且条件适合的情况下才是这样的。
一、缓存应用的场景: 2、对于企业应用,要保证数据的一致性是第一位的,即使数据被修改,最终用户看到的数据与数据库中的数据要时时一致。适合在应用程序持久层上做缓存,Hibernate二级缓存就适合这个场景。
二、频繁更新的数据要不要被缓存:
数据一致性:本文章的数据一致性是指缓存中的数据与数据库中的数据就保持一致,严格的一致。决对没有脏数据。
当你须要数据一致性,而又不能保存数据一致性时,频繁更新的数据就不可以被缓存。不缓存直接操作数据库,就一致了,没有不一致的问题了。 你的Hibernate对数据库不是独占的,有其它程序来修改数据库中的记录,这时Hibernate是不知道的,也会发生数据不一致. 当使用url或sql语句做为KEY来缓存时,一句select 语句查出n个对象,无法在缓存中精准的找到被修改的某一个对象,当修改一个对象时就不能在缓存中精准的找到他,为了保证数据一致性,就要清除缓存中所有的 同类对象,使下次查询时无法命中缓存。而不清除,很有可能发生数据不一致。这相当于Hibernate的查询缓存。这时个不要使用缓存。 因为经常被更新修改的对象,一定也更加经常的被查询,需要缓存他来提高应用程序的性能。如果执行修改sql时,同时锁住缓存中的这个对象并更新他,之后解 锁是最理想的。Hibernate的二级缓存策略,是针对于ID查询的缓存策略,所以可以做到精准的找到缓存中的目标,加之“锁”的帮助,可实现数据一致 性。 但限制也有比如使用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的二级缓存策略,是以ID做为key 的缓存策略,在删除、更新、增加数据的时候,同时更新缓存。 关于是否命中,是使用Statistics类监测的(通过SessionFactory的getStatistics()方法得到)。 总结一下:如果你打算开启hibernate的二级缓存,在修改与删除时,就要使用session.update(),session.delete()方法按ID一条一条的操作,这样对二级缓存是最优的。
1 只读缓存 read only 如果数据是只读的,例如引用数据,那么总是使用“read-only”策略,因为它是最简单、最高效的策略,也是集群安全的策略。是性能第一的策略 。 2 读写缓存 read write 如果你的数据是又读又写的,那么使用“read-write”策略。这通常是性能第三的策略,因为它要求有缓存锁,缓存集群中使用重量级的“推”更新策略。 3 非严格读写缓存 nonstrict read write 如果你的数据读很多或者很少有并发缓存访问和更新,那么可以使用“nonstrict-read-write”策略。感谢它的轻量级“拉”更新策略,它通常是性能第二好的策略。 4 事务缓存 transactional (一定要在JTA环境中) 除非你真的想将缓存更新和数据库更新放在一个JTA事务里,否则不要使用“transactional”策略,因为JTA需要漫长的两阶段提交处理,这导致它基本是性能最差的策略。
下面以JBoss Cache为例说一说锁: 悲观锁:这些锁的隔离级别和数据库实施的隔离级别相同,这种方案简单而且健壮,允许多用户同时读取数据。读操作阻塞写操作,悲观锁的读写是互斥的,无法同时进行的,写的性能不好。 乐观锁: 这个方式则牵涉到数据版本,可以获得高度并发性。那些请求读取数据的用户不会因为并发数据库写入操作而受到阻塞。而且,乐观锁定方式还可以避免悲观锁定中 有可能发生的死锁。但它仍然有两个主要的缺点:一是性能问题。因为不断的将结点的状态拷贝到每个并发线程所造成的内存和 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开销。如果内存有限,那这种情况会很明显。
2、JBOSS CACHE JBoss Cache分布式缓存:Manik Surtani访谈 3、EhCache 4、Infinispan
|
|
来自: Blex > 《Hibernate》