关于Hibernate的缓存

关于Hibernate的缓存

因为项目用到了Hibernate3.x版本,所以不又得重温了一下关于Hibernate的技巧和原理,其中主要是针对它的缓存进行那个了一番调研,于是想写点什么留下一点纪念。

 

(一)  Hibernate的缓存概念

Hibernate是干嘛的不用再解释了,它有两级缓存也是大家都知道的。简单来说一级缓存就是从你拿到Hibernatesession并且open一直到这个session被你用完close之间的周期之内,所有从数据库load的对象都会被以Map格式(IDkeyEntityvalue)缓存起来,由这个session内置来维护,在周期之内需要重复使用一个Entity的时候,第二次就直接从session的一级缓存中去拿,不用再跟数据库交互了。

 

由于大多数应用中session的生命周期都是短暂的,即在某一个方法进入之后获得,方法结束session就在finallyclose了,所以用处并不是那么明显。而Hibernate二级缓存才是真正可配置的分不同策略针对应用逻辑可以发挥重要作用的东西,二级缓存又叫SessionFactory缓存,由于SessionFactory是管理着数据库链接池和Hibernate Session的单实例(分布式除外),它会对数据库load出来的数据进行缓存,同样以Map方式存储。当缓存的数据过期或者数据库表被更新时,二级缓存的数据就会因为失效而被清除。等待下一次由于业务逻辑而触发的查询调用而重组缓存。(具体的原理稍后再分析)

 

(二)  Hibernate的缓存配置使用

一级缓存是session内置的,所以无法配置,本身它的用处也不明显,除非碰到批量处理数据的时候,在每一遍循环结束为了节约内存,可能会用到evict()方法人工即时删除缓存的对象起到人工干预一级缓存的作用。不过这要配合session.flush()方法在删除之前及时持久化数据,因为没有事务时,在session.close()里会自动调用flush(),flush()又可以sql语句发送到数据库

 

二级缓存是需要配置的,否则Hibernate会自动启用defaultCache。配置可以有多种选择,比如ehcacheoscache等。按照使用的方式又分为Class缓存、查询缓存和Collection缓存。

 

Class缓存是指对于一条记录,根据ID来找,缓存的key就是IDvaluePOJO。无论listload还是iterate,只要读出一个对象,都会填充缓存。注意list方法不会使用缓存只从数据库读完了去填充缓存,而iterate会先取数据库select id出来,然后一个id一个idload,每次load如果在缓存里面有,就从缓存取,没有的话再去数据库load。根据不同的缓存实现方式可以在xml中配置的有超时设置、发呆时间、缓存最大条目、超出上限是否要写本地硬盘等等。可以针对每个Class配置,也可以让多个Class共享一种配置。总之Class缓存是针对于ID查询的缓存策略,对于条件查询则毫无作用。

 

那么对于条件查询怎么办?为此,Hibernate专门提供了查询缓存,要使用查询缓存首先需要配置hibernate.cache.use_query_cache=true,同样在xml中也有超时时间、最大条目等配置,使用的时候需要通过query.setCacheable(true); 来激活它。对于查询缓存来说,存储的不是对象,而是查询条件(或者说类似PreparedStatement这样的预编译SQL),所以缓存的key是根据hql生成的sql,再加上参数,分页等信息等,而缓存的value,如果是list方式的话,value在这里并不是整个结果集,而是查询出来的这一串ID。查询缓存前提是已打开相关类的class缓存,然后Querylistiterate方法第一次执行的时候,都是既填充查询缓存又填充class缓存的。

 

Hibernate更新数据库的时候,它怎么知道更新哪些查询缓存呢? Hibernate在一个地方维护每个表的最后更新时间,在2.1版本中也就是 net.sf.hibernate.cache.UpdateTimestampsCache所指定的缓存配置里面。 当通过Hibernate更新的时候,它会知道这次更新影响了哪些表。然后它更新这些表的最后更新时间。每个缓存都有一个生成时间和这个缓存所查询的表,当Hibernate查询一个缓存是否存在的时候,如果缓存存在,它还要取出缓存的生成时间和这个缓存所查询的表,然后去查找这些表的最后更新时间,如果有一个表在生成时间后更新过了,那么这个缓存是无效的。 可以看出,只要更新过一个表,那么凡是涉及到这个表的查询缓存就失效了,因此查询缓存的命中率可能会比较低(谨慎使用) 

 

Collection的缓存和前面查询缓存的list一样,也是只保持一串id,但它不会因为这个表更新过就失效,一个collection缓存仅在这个collection里面的元素有增删时才失效。 这样有一个问题,如果你的collection是根据某个字段排序的,当其中一个元素更新了该字段时,导致顺序改变时,collection缓存里面的顺序没有做更新。 

 

(三)  Hibernate的缓存是否需要

某前辈是这样总结的:不要想当然的以为缓存一定能提高性能,仅仅在你能够驾驭它并且条件合适的情况下才是这样的。hibernate的二级缓存限制还是比较多的,不方便用jdbc可能会大大的降低更新性能。在不了解原理的情况下乱用,可能会有1+N的问题。不当的使用还可能导致读出脏数据。 如果受不了hibernate的诸多限制,那么还是自己在应用程序的层面上做缓存吧。 在越高的层面上做缓存,效果就会越好。就好像尽管磁盘有缓存,数据库还是要实现自己的缓存,尽管数据库有缓存,咱们的应用程序还是要做缓存。因为底层的缓存它并不知道高层要用这些数据干什么,只能做的比较通用,而高层可以有针对性的实现缓存,所以在更高的级别上做缓存,效果也要好些吧。 

 

Ant非常赞同这样的观念,任何技术都是需要能熟练驾驭之后才能在项目中发挥真正的作用。在应用级别做缓存例如Memcache等,也是目前比较流行的策略。Hibernate的缓存虽然做的还算不错,但也不要过于依赖它的缓存而对代码的质量和架构的合理性降低了要求,虽然初期或者暂时不会出现任何问题,但就如古惑仔里经典的台词一样出来混,迟早是要还的!

 

什么样的数据适合存放到第二级缓存中?

1 很少被修改的数据 

2 不是很重要的数据,允许出现偶尔并发的数据 

3 不会被并发访问的数据 

4 参考数据,指的是供应用参考的常量数据,它的实例数目有限,它的实例会被许多其他类的实例引用,实例极少或者从来不会被修改。 

 

不适合存放到第二级缓存的数据? 

1 经常被修改的数据 

2 财务数据,绝对不允许出现并发 

3 与其他应用共享的数据。 

你可能感兴趣的:(数据库,Hibernate,session,list,query,Class)