Hibernate二级缓存 ---- 最佳实践
转Hibernate二级缓存 ---- 最佳实践文章分类:Java编程这时再查询这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访谈