`

hibernate二级缓存攻略(第二天)

阅读更多

查询缓存

首先需要配置

hibernate.cache.use_query_cache = true

 如果用ehcache,配置ehcache.xml,注意hibernate3.0以后不是net.sf的包名了

< cache name = " net.sf.hibernate.cache.StandardQueryCache "  
    maxElementsInMemory
= " 50 "  
    eternal
= " false "  
    timeToIdleSeconds
= " 3600 "  
    timeToLiveSeconds
= " 7200 "  
    overflowToDisk
= " true "
/>
 

< cache name = " net.sf.hibernate.cache.UpdateTimestampsCache "  
    maxElementsInMemory
= " 5000 "  
    eternal
= " true "  
    overflowToDisk
= " true "
/>
 

然后

query.setCacheable( true ); // 激活查询缓存 
query.setCacheRegion( " myCacheRegion " ); // 指定要使用的cacheRegion,可选 

第二行指定要使用的cacheRegion是myCacheRegion,即你可以给每个查询缓存做一个单独的配置,使用setCacheRegion来做这个指定,需要在ehcache.xml里面配置它:

< cache name = " myCacheRegion "  
    maxElementsInMemory
= " 10 "  
    eternal
= " false "  
    timeToIdleSeconds
= " 3600 "  
    timeToLiveSeconds
= " 7200 "  
    overflowToDisk
= " true "  
/>  

如果省略第二行,不设置cacheRegion的话,那么会使用上面提到的标准查询缓存的配置,也就是net.sf.hibernate.cache.StandardQueryCache

对于查询缓存来说,缓存的key是根据hql生成的sql,再加上参数,分页等信息(可以通过日志输出看到,不过它的输出不是很可读,最好改一下它的代码)。

比如hql:

from Cat c where c.name like ?

生成大致如下的sql:

select * from cat c where c.name like ?

参数是"tiger%",那么查询缓存的key*大约*是这样的字符串(我是凭记忆写的,并不精确,不过看了也该明白了):

select * from cat c where c.name like ? , parameter:tiger%

这样,保证了同样的查询、同样的参数等条件下具有一样的key。

现在说说缓存的value,如果是list方式的话,value在这里并不是整个结果集,而是查询出来的这一串ID。也就是说,不管是list方法还是iterate方法,第一次查询的时候,它们的查询方式很它们平时的方式是一样的,list执行一条sql,iterate执行1+N条,多出来的行为是它们填充了缓存。但是到同样条件第二次查询的时候,就都和iterate的行为一样了,根据缓存的key去缓存里面查到了value,value是一串id,然后在到class的缓存里面去一个一个的load出来。这样做是为了节约内存。

可以看出来,查询缓存需要打开相关类的class缓存。list和iterate方法第一次执行的时候,都是既填充查询缓存又填充class缓存的。

这里还有一个很容易被忽视的重要问题,即打开查询缓存以后,即使是list方法也可能遇到1+N的问题!相同条件第一次list的时候,因为查询缓存中找不到,不管class缓存是否存在数据,总是发送一条sql语句到数据库获取全部数据,然后填充查询缓存和class缓存。但是第二次执行的时候,问题就来了,如果你的class缓存的超时时间比较短,现在class缓存都超时了,但是查询缓存还在,那么list方法在获取id串以后,将会一个一个去数据库load!因此,class缓存的超时时间一定不能短于查询缓存设置的超时时间!如果还设置了发呆时间的话,保证class缓存的发呆时间也大于查询的缓存的生存时间。这里还有其他情况,比如class缓存被程序强制evict了,这种情况就请自己注意了。

另外,如果hql查询包含select字句,那么查询缓存里面的value就是整个结果集了。

当hibernate更新数据库的时候,它怎么知道更新哪些查询缓存呢?

hibernate在一个地方维护每个表的最后更新时间,其实也就是放在上面net.sf.hibernate.cache.UpdateTimestampsCache所指定的缓存配置里面。

当通过hibernate更新的时候,hibernate会知道这次更新影响了哪些表。然后它更新这些表的最后更新时间。每个缓存都有一个生成时间和这个缓存所查询的表,当hibernate查询一个缓存是否存在的时候,如果缓存存在,它还要取出缓存的生成时间和这个缓存所查询的表,然后去查找这些表的最后更新时间,如果有一个表在生成时间后更新过了,那么这个缓存是无效的。

可以看出,只要更新过一个表,那么凡是涉及到这个表的查询缓存就失效了,因此查询缓存的命中率可能会比较低。

Collection缓存

需要在hbm的collection里面设置

<cache usage="read-write"/>

假如class是Cat,collection叫children,那么ehcache里面配置

<cache name="com.xxx.pojo.Cat.children"
maxElementsInMemory="20" eternal="false" timeToIdleSeconds="3600" timeToLiveSeconds="7200"
overflowToDisk="true" />

Collection的缓存和前面查询缓存的list一样,也是只保持一串id,但它不会因为这个表更新过就失效,一个collection缓存仅在这个collection里面的元素有增删时才失效。

这样有一个问题,如果你的collection是根据某个字段排序的,当其中一个元素更新了该字段时,导致顺序改变时,collection缓存里面的顺序没有做更新。

缓存策略

只读缓存(read-only):没有什么好说的

读/写缓存(read-write):程序可能要的更新数据

不严格的读/写缓存(nonstrict-read-write):需要更新数据,但是两个事务更新同一条记录的可能性很小,性能比读写缓存好

事务缓存(transactional):缓存支持事务,发生异常的时候,缓存也能够回滚,只支持jta环境,这个我没有怎么研究过

读写缓存和不严格读写缓存在实现上的区别在于,读写缓存更新缓存的时候会把缓存里面的数据换成一个锁,其他事务如果去取相应的缓存数据,发现被锁住了,然后就直接取数据库查询。

在hibernate2.1的ehcache实现中,如果锁住部分缓存的事务发生了异常,那么缓存会一直被锁住,直到60秒后超时。

不严格读写缓存不锁定缓存中的数据。

使用二级缓存的前置条件

你的hibernate程序对数据库有独占的写访问权,其他的进程更新了数据库,hibernate是不可能知道的。你操作数据库必需直接通过hibernate,如果你调用存储过程,或者自己使用jdbc更新数据库,hibernate也是不知道的。hibernate3.0的大批量更新和删除是不更新二级缓存的,但是据说3.1已经解决了这个问题。

这个限制相当的棘手,有时候hibernate做批量更新、删除很慢,但是你却不能自己写jdbc来优化,很郁闷吧。

SessionFactory也提供了移除缓存的方法,你一定要自己写一些JDBC的话,可以调用这些方法移除缓存,这些方法是:
void evict(Class persistentClass)
Evict all entries from the second-level cache.
void evict(Class persistentClass, Serializable id)
Evict an entry from the second-level cache.
void evictCollection(String roleName)
Evict all entries from the second-level cache.
void evictCollection(String roleName, Serializable id)
Evict an entry from the second-level cache.
void evictQueries()
Evict any query result sets cached in the default query cache region.
void evictQueries(String cacheRegion)
Evict any query result sets cached in the named query cache region.

不过我不建议这样做,因为这样很难维护。比如你现在用JDBC批量更新了某个表,有3个查询缓存会用到这个表,用evictQueries(String cacheRegion)移除了3个查询缓存,然后用evict(Class persistentClass)移除了class缓存,看上去好像完整了。不过哪天你添加了一个相关查询缓存,可能会忘记更新这里的移除代码。如果你的jdbc代码到处都是,在你添加一个查询缓存的时候,还知道其他什么地方也要做相应的改动吗?

----------------------------------------------------

总结:

不要想当然的以为缓存一定能提高性能,仅仅在你能够驾驭它并且条件合适的情况下才是这样的。hibernate的二级缓存限制还是比较多的,不方便用jdbc可能会大大的降低更新性能。在不了解原理的情况下乱用,可能会有1+N的问题。不当的使用还可能导致读出脏数据。

如果受不了hibernate的诸多限制,那么还是自己在应用程序的层面上做缓存吧。

在越高的层面上做缓存,效果就会越好。就好像尽管磁盘有缓存,数据库还是要实现自己的缓存,尽管数据库有缓存,咱们的应用程序还是要做缓存。因为底层的缓存它并不知道高层要用这些数据干什么,只能做的比较通用,而高层可以有针对性的实现缓存,所以在更高的级别上做缓存,效果也要好些吧。

分享到:
评论

相关推荐

    hibernate学习笔记第二天的源码

    在深入探讨Hibernate学习笔记第二天的源码之前,我们先来理解一下Hibernate的核心概念。Hibernate是一个开源的对象关系映射(ORM)框架,它允许Java开发者将数据库操作转化为对象模型,大大简化了数据访问层的开发...

    hibernate培训第二天

    【hibernate培训第二天】——深入理解Hibernate框架 在软件开发中,ORM(Object-Relational Mapping)框架已经成为连接数据库与应用程序的重要桥梁,而Hibernate作为Java领域中的主流ORM框架,其强大而灵活的功能...

    hibernate第一天笔记

    而二级缓存则是在`SessionFactory`级别,虽然现在更多地被Redis等分布式缓存替代,但在某些场景下仍有一定价值。 验证一级缓存的存在,可以通过多次执行`session.get()`来观察结果。第一次获取对象时,Hibernate会...

    hibernate培训第四天

    9. **缓存机制**:为了提高性能,Hibernate引入了缓存机制,包括第一级缓存(Session缓存)和第二级缓存(可选)。理解缓存的工作原理和配置,可以有效优化应用程序性能。 10. **事务管理**:在Java应用中,事务...

    Hibernate5.0-4天学习笔记.rar

    在缓存策略方面,Hibernate提供了第一级缓存(Session缓存)和第二级缓存(SessionFactory缓存),以及查询缓存,它们可以显著提高应用的响应速度。合理地利用缓存策略,可以减少数据库的访问次数,提升系统性能。 ...

    hibernate培训第三天

    二级缓存则可配置为全局共享,可以使用第三方缓存解决方案如Ehcache。理解缓存的工作原理以及如何配置和优化缓存是提升系统性能的关键。 **六、关联映射** hibernate支持多种关联映射,如一对一(@OneToOne)、一对...

    2017黑马程序员第一天Hibernate讲义,笔记,源码

    6. **缓存机制**:Hibernate提供了一级缓存(Session级别的)和二级缓存(SessionFactory级别的),提高了数据读取效率。 7. **对象状态**:Hibernate区分了瞬时、持久化、托管和脱管四种对象状态,理解这些状态...

    hibernate开发jar包

    8. **第二级缓存**:Hibernate支持缓存机制,提高性能,第二级缓存可以跨会话共享,可以使用第三方缓存实现,如EhCache。 9. **事务管理**:Hibernate提供了集成的事务处理机制,可以基于JTA或JDBC进行事务控制。 ...

    hibernate培训第一天

    7. **缓存机制**:为了提高性能,Hibernate提供了两级缓存,一级缓存在Session级别,二级缓存可以跨Session共享。缓存策略的选择和配置也是学习的重点。 8. **事务管理**:在Hibernate中,事务是数据库操作的基本...

    hibernate3.3.2 jar

    8. **缓存机制**: Hibernate3.3.2引入了第一级缓存和第二级缓存,提高了数据访问效率。第一级缓存是Session级别的,每个Session都有一个本地缓存;第二级缓存则可以跨Session共享,通常由插件如Ehcache实现。 9. **...

    hibernate3.6.10.Final

    6. **缓存机制**:一级缓存由SessionFactory管理,二级缓存可配置第三方缓存服务如Ehcache,提高性能。 7. **事件监听**:可以通过实现特定接口或者使用注解,实现在对象生命周期中的不同阶段执行自定义逻辑。 8. **...

    hibernate达内五天复习笔记.pdf

    - 第二天可能涉及了更具体的映射配置和实例,如XML配置文件或注解方式的映射。 - 第三天可能深入到对象状态的转换和生命周期管理,以及如何处理事务。 - 第四天可能讲解了HQL的使用,包括查询、更新、删除等操作...

    hibernate五天学习笔记(txt)

    Hibernate提供了第一级缓存和第二级缓存,可以显著提高应用性能。我们还会学习如何处理并发问题,如乐观锁和悲观锁的使用,以及事务的管理,包括@Transactional注解的使用和事务的隔离级别。 此外,笔记可能还会...

    黑马程序员_hibernate框架开发2016版讲义和笔记资料_day1_day2_day3_day4

    5.1 第二级缓存:利用Hibernate的二级缓存提高性能,例如使用EHCache或Infinispan。 5.2 lazy loading:延迟加载策略,只在真正需要时加载关联对象,避免内存消耗过大。 5.3 分页查询:使用Criteria的...

    黑马程序员_hibernate框架开发2016版讲义和笔记资料_day02

    本文将深入探讨2016年黑马程序员发布的Hibernate框架开发课程的第二天内容。Hibernate,一个强大的Java持久化框架,简化了数据库操作,为开发者提供了对象关系映射(ORM)解决方案。在Day02的学习中,我们将重点关注...

    黑马程序员hibernate框架开发2016版视频day1-day4源码笔记

    Hibernate提供了第一级缓存和第二级缓存,它们可以显著提高数据访问速度。学习如何配置和使用这些缓存策略是提升应用性能的关键。此外,还会讨论如何避免常见的性能瓶颈,如N+1查询问题,以及如何利用Hibernate的...

    传智博客Hibernate框架2016版笔记资料day03~04

    6. **缓存策略**:Hibernate提供了第一级缓存和第二级缓存,这部分内容可能会讲解如何配置和使用它们,以提高数据访问效率。 7. **高级特性**:可能还会涉及到一对一、一对多、多对一、多对多的关联映射,以及懒...

    hibernate学习笔记第三天的源码

    9. **缓存(Cache)**:Hibernate提供了两级缓存机制,一级缓存在Session级别,二级缓存则跨Session共享。缓存可以提高性能,减少对数据库的访问。 10. **事务管理(Transaction)**:Hibernate支持JTA(Java ...

    传智博客Hibernate框架2016版笔记资料day01~02

    7. **缓存机制**:解释Hibernate的第一级缓存和第二级缓存,以及如何配置和使用它们来提高性能。 8. **事务管理**:讨论Hibernate中的事务处理,包括自动提交、手动提交和回滚,以及事务隔离级别的概念。 通过深入...

Global site tag (gtag.js) - Google Analytics