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

来源:互联网 发布:阿里云cdn怎么设置 编辑:程序博客网 时间:2024/06/05 12:50
 

几月前, 我接到一个银行的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>  

里面的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>  

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

尤其当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"/>  

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


自己看了一眼,的确没说清楚,要清楚的几个概念是cacheFacade.CachingModel.FlushingModel.CachingInterceptor.FlushingInterceptor.这些算是比较重要的。其他aop内容略。caching拦截器拦截某方法,根据cachingModel定义的规则由cacheFacade将之放入cache。flushing拦截器拦截到某方法,根据flushingModel定义的规则由cacheFacade将对应的cache清除。嗯,基本上流程就这样子了。
原创粉丝点击