Spring,Hibernate,Struts2性能优化(一):Hibernate缓存性能优化

 

几月前, 我接到一个银行的J2EE应用系统JSF+HIBERNATE+SPRING+WebService性能优化合同,静下心来几个月好好研究了一下J2EE 系统的关键性能优化问题,结果非常令人吃惊,提交测试后其并发运行性能得到几倍到几十倍的提高,我想把其中的一些关键技术点总结出来,与大家共享,今天就 贡献最重要的部分,HIBERNATE性能优化,以后再分别讨论JSF的分页技术,SPRING的WebServiceTemplate的应用,这些也都 是IBM公司目前正在全球推广使用的最前沿的技术。
对Hibernate优化来讲,关键点包括the second-level cache 如ehcache(OScache,JBosscache), 数据fetch策略以避免太密集的数据库query accesses 和增进并发性.  
开发环境是:IBM Websphere 6.1server/RAD7/IBM DB2 9/Spring 2.5/Hibernate 3.0/JSF.
1.首先建立ehcache, 我做了一下几件事:
a)在ApplicationContext.xml文件中增加一些配置:

...

< bean id= "hibernateSessionID"
        class= "org.springframework.orm.hibernate3.LocalSessionFactoryBean" >
    < property name= "hibernateProperties" >
        < props>
           ……
           < prop key= "hibernate.cache.provider_class" > org. hibernate. cache. EhCacheProvider< / prop>
           < prop key= "hibernate.cache.use_second_level_cache" > True< / prop>
           < prop key= "hibernate.cache.use_query_cache" > true< / prop> < / props>
    < / property>
< / bean>
< bean id= "cacheManager"  

        class= "org.springframework.cache.ehcache.EhCacheManagerFactoryBean" >  
    < property name= "configLocation" value= "/WEB-INF/ehcache.xml" / >  
< / bean>  
< !-- A facade to the Ehcache cache class -->  
< bean id= "cacheProviderFacade"

        class= "org.springmodules.cache.provider.ehcache.EhCacheFacade" >  
    < property name= "cacheManager" ref= "cacheManager" / >  
< / bean>

...

b) 在ORM映射文件中增加属性:

< cache usage = "read-write" / >

  CustomerOrder.hbm.xml;
  CustomerOffer.hbm.xml;
  Customer.hbm.xml
  CustomerAccount.hbm.xml   
 ........
c) 在Web Application Library 目录增加一些类库:
  Spring-modules-cache.jar ; ehcache.jar 
d) 把文件 ehcache.xml 放在 WEB-INF/ehcache.xml

< ehcache>
    < diskStore path= "java.io.tmpdir" / >
    < defaultCache
        maxElementsInMemory= "10000"
        eternal= "false"
        timeToIdleSeconds= "120"
        timeToLiveSeconds= "120"
        overflowToDisk= "true"
     / >
    < cache name= "com.???.???.domain.CustomerOrder"
        maxElementsInMemory= "50"
        eternal= "false"
        timeToIdleSeconds= "100"
        timeToLiveSeconds= "100"
        overflowToDisk= "false"
     / >
< / ehcache>


e) 对查询结果 QueryResult cache, 如下修改源码:

CustomerDAO.java:
........
queryObject.setCacheable(true);
return queryObject.list();
.........
2. 如何得到 eh-cache 统计命中率:
a)   在 ApplicationContext.xml增加配置:

< bean id= "hibernateSessionID" class= "org.springframework.orm.hibernate3.LocalSessionFactoryBean" >
        < property name= "hibernateProperties" >
            < props>
                ……
                < prop key= "hibernate.generate_statistics" > true< / prop>
                
            < / props>
< / property>
< / bean>

b) 修改package com.xxx.yyy.dao.impl中类CustmerOrderExtDAO.java的方法queryWithPaging: 
private List queryWithPaging(final String queryStr,
    final String[] paramNames,
        final Object[] values,
    final int pageNum,
        final int maxRowsPerPage){
        ……                       
    Statistics stats =getHibernateTemplate().getSessionFactory().getStatistics();
    long l2HitCount = stats.getSecondLevelCacheHitCount();
    long l2MissCount = stats.getSecondLevelCacheMissCount();
    long queryHitCount = stats.getQueryCacheHitCount();
    long queryMissCount = stats.getQueryCacheMissCount();
    System.out.println("L2_Cache_Hit :"+l2HitCount);
    System.out.println("L2_Cache_Miss :"+l2MissCount);       
    double l2CacheHitRatio = l2HitCount / (l2HitCount + l2MissCount + 0.000001);
    System.out.println("L2_Cache_Hit_Ratio :"+l2CacheHitRatio);   
    System.out.println("");       
    System.out.println("Query_Cache_Hit :"+queryHitCount);
    System.out.println("Query_Cache_Miss :"+queryMissCount);   
    ……
        Return result
 }
c)模拟多个客户并发访问类似的数据,我们得到很好的结果:
EH cache 命中率逐渐收敛到接近75%左右。
 
3. Hibernate fetch strategy(solve N+1 select problem):
a)  在文件 ApplicationContext.xml中增加配置:

......
< bean id= "hibernateSessionID" class= "org.springframework.orm.hibernate3.LocalSessionFactoryBean" >
        < property name= "hibernateProperties" >
            < props>
                ……
          < prop key= "hibernate.cache.use_minimal_puts" > true< / prop>
                < prop key= "hibernate.use_outer_join" > true< / prop>
                < prop key= "hibernate.max_fetch_depth" > 3< / prop> < / props>
< / property>
< / bean>
......

b) 在Hibernate 映射文件中配置属性 fetch = “join”:
  CustomerOrder.hbm.xml;
  CustomerOffer.hbm.xml;
  Customer.hbm.xml
  CustomerAccount.hbm.xml   
 ........
<set name="CustOrders"  lazy="false" fetch="join" inverse="true">
<set name="orderStatuses"   lazy="false" fetch="join" inverse="true" order-by="STATUS_TS desc" >
<set name="orderAccounts"   lazy="false" fetch="join" inverse="true">
........
c)看结果,当我们通过以下配置打开数据库SQL query时:
        <prop key="hibernate.show_sql">true</prop>
我们能够看到hibernate比以前只需要更少的数据库访问次数,因为它能够在二级EHcache中找到大部分的数据。

 

===============================================================================

项目需要缓存,google了一下,发现spring module有现成的东西可用,随即拿来看看。发现还是比较好用的。
改cache采用了aop的方式进行cache的写入和刷出。使用spring风格,上手容易。
系统负载不高,因此对于该缓存方法的性能等等不做评价,目前满足需要。
使用方法:
有这么几个概念需要提前知道1.XXCacheFacade,比如如果是oscache,那么XX就是oscachefacade.该类负责缓存的写入和刷出

Java代码   收藏代码
  1. <bean id= "oscacheFacade"   class = "org.springmodules.cache.provider.oscache.OsCacheFacade" >  
  2.     <property name="failQuietlyEnabled"  value= "true" />  
  3.     <property name="cacheManager" >  
  4.         <bean id="oscacheManager"   class = "org.springmodules.cache.provider.oscache.OsCacheManagerFactoryBean" />  
  5.     </property>  
  6.   </bean>  
<bean id="oscacheFacade" class="org.springmodules.cache.provider.oscache.OsCacheFacade">
  	<property name="failQuietlyEnabled" value="true"/>
  	<property name="cacheManager">
  		<bean id="oscacheManager" class="org.springmodules.cache.provider.oscache.OsCacheManagerFactoryBean"/>
  	</property>
  </bean>

里面的cacheManager必须要有,改类负责提供底层具体的cache实现,比如oscache或者EHcache等。
2.MethodMapCachingInterceptor这个拦截器是官方提供的同类型的拦截器之一,根据方法名,参数匹配拦截。
Java代码   收藏代码
  1. <bean id= "cachingInterceptor001"   class = "org.springmodules.cache.interceptor.caching.MethodMapCachingInterceptor" >  
  2.     <property name="cacheProviderFacade"  ref= "oscacheFacade" />  
  3.     <property name="cachingModels" >  
  4.         <props>  
  5.             <prop key="com.company.jncz.TestItIF.get*" >groups=aa;refreshPeriod= 10 </prop>  
  6.             <prop key="com.company.jncz.TestItIF.load*" >groups=bb;refreshPeriod= 10 </prop>  
  7.         </props>  
  8.     </property>  
  9.   </bean>  
<bean id="cachingInterceptor001" class="org.springmodules.cache.interceptor.caching.MethodMapCachingInterceptor">
  	<property name="cacheProviderFacade" ref="oscacheFacade"/>
  	<property name="cachingModels">
  		<props>
  			<prop key="com.company.jncz.TestItIF.get*">groups=aa;refreshPeriod=10</prop>
  			<prop key="com.company.jncz.TestItIF.load*">groups=bb;refreshPeriod=10</prop>
  		</props>
  	</property>
  </bean>

注意cachingModels。有两种方式写法,一种是上面看到的使用props另一种是使用Map.在有些情况下只能使用Map方式,下面解释
Java代码   收藏代码
  1.   <map>  
  2.   <entry key="com.company.jncz.TestIt.get*" >  
  3.     <ref local="oscacheCachingModel" />  
  4.   </entry>  
  5. </map>  
  	  <map>
	    <entry key="com.company.jncz.TestIt.get*">
	      <ref local="oscacheCachingModel"/>
	    </entry>
	  </map>

Java代码   收藏代码
  1. <bean id= "oscacheCachingModel"   class = "org.springmodules.cache.provider.oscache.OsCacheCachingModel" >  
  2.     <property name="groups" >  
  3.             <list>  
  4.                <value>aa</value>  
  5.                <value>bb</value>  
  6.             </list>  
  7.        </property>  
  8.     <property name="refreshPeriod"  value= "10" />  
  9.  </bean>  
 <bean id="oscacheCachingModel" class="org.springmodules.cache.provider.oscache.OsCacheCachingModel">
  	<property name="groups">
             <list>
                <value>aa</value>
                <value>bb</value>
             </list>
        </property>
  	<property name="refreshPeriod" value="10"/>
  </bean>

尤其当groups(对于oscache来说是groups)的值不止一个的时候,就需要使用map的方式。
否则不生效(也许还有什么我没有注意到).另外需要注意的是对于model来说他的key很重要。有以下需要注意:如果AImpl是A接口的实现 类,而且你在其他地方使用时都是用A接口来声明的,那么key就必须写成接口的全限定名比如:com.company.jncz.A.getXX,否则无 法识别。

对于与cachingModel相对应的flushingModel写法是类似的,参考api很容易写出来。

最后
Java代码   收藏代码
  1. <bean  class = "org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator" >  
  2.         <property name="beanNames" >  
  3.             <value>*Service</value>  
  4.         </property>  
  5.         <property name="interceptorNames" >  
  6.             <list>  
  7.                 <value>cachingInterceptor001</value>  
  8.                 <value>flushingInterceptor001</value>  
  9.             </list>  
  10.         </property>  
  11.   </bean>  
  12.   <bean id="testService"   class = "com.company.jncz.TestItStub" />  
<bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
		<property name="beanNames">
			<value>*Service</value>
		</property>
		<property name="interceptorNames">
			<list>
				<value>cachingInterceptor001</value>
				<value>flushingInterceptor001</value>
			</list>
		</property>
  </bean>
  <bean id="testService" class="com.company.jncz.TestItStub"/>

这些不做介绍。
总之比较简单。这两天感冒,昏昏沉沉,不知道有没有表达清楚。。。。


自己看了一眼,的确没说清楚,要清楚的几个概念是 cacheFacade.CachingModel.FlushingModel.CachingInterceptor.FlushingInterceptor. 这些算是比较重要的。其他aop内容略。caching拦截器拦截某方法,根据cachingModel定义的规则由cacheFacade将之放入 cache。flushing拦截器拦截到某方法,根据flushingModel定义的规则由cacheFacade将对应的cache清除。嗯,基本 上流程就这样子了。
这是一个实际使用中的项目,可访问 http://cdbke.cuit.edu.cn 可查看效果及功能,后台需要登录后才能使用(出于安全性考虑这里就不给大家介绍了,另外还有一个struts1版本的,如有需要可以与我联系)。 此项目整合了目前主流和最前源的web开发技术:采用ehcache实现二级缓存(包含查询缓存);用sf4j及logback(log4j的升级版)记录日志;proxool(据说是dbcp和c3p0三者中最优秀的)做连接池;使用jquery的ajax实现仿google人名自动补全;头像上传剪切压缩处理。 包含有完整的jar包和源代码,可以直接下载编译部署和运行,这是专门为我们实验室定制开发的。虽然后台逻辑并不复杂,但已经包含了架构基于s2sh技术型系统的全部基础部分:如分页,缓存,文件上传,连接池等。很适合学习使用,希望对初学JavaEE WEB开的人有所帮助。 这个资源在去年发布了第一版,已经有很多朋友下了觉得对他们有帮助,所以我才再发了一个第二版,希望对有需要的朋友有所帮助。本版本全面更新了jar包,全部使用了当前最新版本的jar包,struct2.1.8 spring3 hibernate3.5,全面使用注解取代xm的l配置。 另外增加了一个ant构建脚本,支持使用hudson完成每日构建,持续集成,自动测试,代码规范检查,代码审查等功能(与此相关的jar包由于上传文件大小限制未导入) 本系统一直在使用中,所以还会不段更新,之后我打算再做一个基于javaee6的实现,给需要想学习ejb3.1等技术的朋友一个参考实现 集成测试相关jar包和更新构建脚本下载:http://download.csdn.net/source/2427972
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值