性能优化之Hibernate缓存讲解、应用和调优

标签: 性能优化 hibernate 缓存 | 发表时间:2014-04-11 04:54 | 作者:tcl_6666
出处:http://blog.csdn.net

    近来坤哥推荐我我们一款性能监控、调优工具——JavaMelody,通过它让我觉得项目优化是看得见摸得着的,优化有了针对性。而无论是对于分布式,还是非分布,缓存是提示性能的有效工具。

    数据层是EJB3.0实现的,而EJB3.0内部也是通过Hibernate实现的,而Hibernate本身提供了很好的缓存机制,我们只需要学会使用它驾驭它就够了。

    缓存的机能可以简单理解为将从数据库中访问的数据放在内存中,在以后再次使用到这些数据时可以直接从内存中读取而不必要再次访问数据库,尽量减少和数据库的交互提高性能。

    

概念讲解

在hibernate中提供了二种缓存机制:一级缓存、二级缓存,因为二级缓存策略是针对于ID查询的缓存策略,对于条件查询则毫无作用,为此,Hibernate提供了针对条件查询的Query Cache(查询缓存)。

  一、一级缓存:

     一级缓存是hibernate自带的,不受用户干预,其生命周期和session的生命周期一致,当前session一旦关闭,一级缓存就会消失,因此,一级缓存也叫session缓存或者事务级缓存, 一级缓存只存储实体对象,不会缓存一般的对象属性,即:当获得对象后,就将该对象缓存起来,如果在同一个session中再去获取这个对象时,它会先判断缓存中有没有这个对象的ID,如果有,就直接从缓存中取出,否则,则去访问数据库,取了以后同时会将这个对象缓存起来。

 

二、二级缓存:

 二级缓存也称为进程缓存或者sessionFactory级的缓存,它可以被所有的session共享,二级缓存的生命周期和sessionFactory的生命周期一致, 二级缓存也是只存储实体对象

二级缓存的一般过程如下:

     ①:条件查询的时候,获取查询到的实体对象

     ②:把获得到的所有数据对象根据ID放到二级缓存中

     ③:当Hibernate根据ID访问数据对象时,首先从sesison的一级缓存中查,查不到的时候如果配置了二级缓存,会从二级缓存中查找,如果还查不到,再查询数据库,把结果按照ID放入到缓存中

     ④:进行delete、update、add操作时会同时更新缓存

 

三、查询缓存:

查询缓存是对普通属性结果集的缓存,对实体对象的结果集只缓存id,对于经常使用的查询语句,如果启用了查询缓存,当第一次执行查询语句时,Hibernate会把查询结果存放在二级缓存中,以后再次执行该查询语句时,只需从缓存中获得查询结果,从而提高查询性能, 查询缓存中以键值对的方式存储的, key 键为查询的条件语句(具体的 key 规则应该是:类名 + 方法名 + 参数列表), value 为查询之后等到的结果集的 ID 列表

 

查询缓存的一般过程如下:

     ①:Query Cache保存了之前查询执行过的SelectSQL,以及结果集等信息组成一个Query Key

     ②:当再次遇到查询请求的时候,就会根据QueryKey从QueryCache中找,找到就返回,但当数据表发生数据变动的话,hbiernate就会自动清除QueryCache中对应的Query Key

    我们从查询缓存的策略中可以看出,Query Cache只有在特定的条件下才会发挥作用,而且要求相当严格:

     ①:完全相同的SelectSQL重复执行

     ②:重复执行期间,QueryKey对应的数据表不能有数据变动


启用缓存的配置

EJB中配置查询缓存和二级缓存

1、在persistence.xml中启用缓存

<persistence-unitname="gxpt-qx-entity" transaction-type="JTA" >
<!--对jpa进行性能测试 -->
<provider>net.bull.javamelody.JpaPersistence</provider>
 
                 <jta-data-source>java:/MySqlDS</jta-data-source>
                 <!--<jta-data-source>java:/MyOracleDS</jta-data-source>   -->
                 
                 <properties> 
                          <!-- <propertyname="hibernate.dialect"value="org.hibernate.dialect.Oracle10gDialect"/> -->
                          <propertyname="hibernate.dialect"value="org.hibernate.dialect.MySQLDialect"/>
<propertyname="hibernate.hbm2ddl.auto" value="update" />
<propertyname="hibernate.show_sql" value="true" />
 
<!--指定二级缓存产品的提供商 -->
<propertyname="hibernate.cache.provider_class"
value="net.sf.ehcache.hibernate.SingletonEhCacheProvider"/>
<!--                        <propertyname="hibernate.cache.region.factory_class"
value="org.hibernate.cache.ehcache.EhCacheRegionFactory"/>-->
<!--开启二级缓存 -->
<propertyname="hibernate.cache.use_second_level_cache"value="true"/>
<!--开启查询缓存 -->
<propertyname="hibernate.cache.use_query_cache"value="true"/> 
<!--指定缓存配置文件位置   -->
<propertyname="hibernate.cache.provider_configuration_file_resource_path"
value="/ehcache.xml"/>
 
                 </properties>
</persistence-unit>

 

2、通过注解指定User类使用二级缓存

@Entity
@Table(name="tq_user")
@Cache(usage=CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
publicclass User

 

3、开启查询缓存,除了在persistence.xml中有以上配置外,还需要在底层代码手动开启查询缓存

query.setHint("org.hibernate.cacheable", true);

或者query.setCacheable(true);

 

性能测试

通过sql语句

1、二级缓存关闭时,查询缓存开启和关闭情况对比*****普通属性查询

    

public String myCache() {
        List<String> strings = this.userServiceImpl
                .search("selectu.username from User u where id<4 order by id asc");
        for (String str : strings){
           System.out.println("username:" + str);
        }
 
        System.out.println("===================================");
        List<String> strings2 =this.userServiceImpl
                .search("select u.usernamefrom User u where id<4 order by id asc");
        for (String str : strings2){
           System.out.println("username:" + str);
        }
        return "/mycache";
    }

 

     当前二级缓存为关闭状态,看看查询缓存关闭时的查询结果:

  

  public List<String> search(Stringhql) {
        List<String> rtnStrs = newArrayList<String>();
        try {
           Session session = this.sessionFactory.openSession();
            session.beginTransaction();
 
           Query query = session.createQuery(hql);
            //query.setCacheable(true);//手动开启查询缓存
            rtnStrs =(List<String>) query.list();
 
           session.getTransaction().commit();
        } catch (Exception e){
            System.out.println("DAO层根据HQL语句查询失败");
        }
        return rtnStrs;
    }

   上面代码中屏蔽了query.setCacheable(true)。

    

    关闭二级缓存、关闭查询缓存 运行如下:


 

   开启查询缓存、关闭二级缓存运行如下


结论:对于查询普通属性,无论二级缓存是否开启,只要开启了查询缓存,当两次执行的sql语句相同时,第二次不会发出sql语句,直接从内存中获取。

 

2、查询缓存开启时,二级缓存打开和关闭情况对比*******查询实体对象

   

/**
     * 查询缓存开启,二级缓存关闭*******查询实体对象
     *
     *运行结果:如果关闭查询缓存和二级缓存,在两次查询时都发出sql语句,此时为两条查询语句
     *
     *运行结果:如果开启查询缓存,关闭二级缓存,第二次会发出根据ID查询实体的n条查询语句
     *
     *运行结论:第一次执行list时,会把查询对象的ID缓存到查询缓存中,第二次执行list时(两次的查询SQL语句相同),会遍历查询缓存中的ID到
     * (一级、二级)缓存里找实体对象, 此时没有,则发出查询语句到数据库中查询
     *
    */        
publicString mycache3() {
List<User>users1 = this.userServiceImpl.search();
           for(User u : users1) {
              System.out.println("users1:username:"+ u.getUsername());
          }
   System.out.println("===============");
 
        List<User> users2 =this.userServiceImpl.search();
        for (User u : users2) {
           System.out.println("users2:usersname:" + u.getUsername());
        }
return"/mycache";
    }

 

开启查询缓存、关闭二级缓存 运行如下:(两次都发出sql,而且第二次发出n条语句)



 

开启查询缓存、关闭二级缓存 运行如下(只发出一条语句)


 

总结:

(1)、当只是用hibernate查询缓存,而关闭二级缓存的时候:

①如果查询的是部分属性结果集,那么当第二次查询的时候就不会发出SQL语句,直接从Hibernate查询缓存中取数据

②如果查询的是实体结果集(eg.from User)这个HQL,那么查询出来的实体,首先hibernate查询缓存存放实体的ID,第二次查询的时候,就到hibernate查询缓存中取出ID一条一条的到数据库查询,这样将发出N条SQL语句,造成SQL泛滥。所以,在使用查询缓存的时候,最好配合开启二级缓存。

 

(2)、当开启Hibernate查询缓存和二级缓存的时候:

①如果查询的是部分属性结果集,这个和上面只用hbiernate查询缓存而关闭二级缓存的时候一致,因为不涉及实体,不会用到二级缓存。

②如果查询的是实体结果集,那么查询出来的实体首先在查询缓存中存放实体的ID,并将实体对象保存到二级缓存中,第二次查询的时候,就到hibernate查询缓存中取ID,根据ID去二级缓存中匹配数据,如果有数据就不会发出sql语句,如果都有,第二次查询一条SQL语句都不会发出,直接从二级缓存中取数据。


通过Javamelody

JavaMelody能够在运行环境中监测Java或Java EE应用程序服务器。并以图表的形式显示:Java内存和Java CPU使用情况,用户Session数量,JDBC连接数,和http请求、sql请求、jsp页面与业务接口方法(EJB3、Spring、Guice)的执行数量,平均执行时间,错误百分比等。

坤哥博客有介绍 Java项目性能监控和调优工具-Javamelody

 

监控项目缓存个数

这是本次开发的权限项目中的缓存,共有12个,其中红色部分分别为二级缓存和查询缓存


 

对spring的监控

加缓存情况


不加缓存情况


 

根据统计结果,发现缓存的确可以提高性能。

但是有时候使用了缓存反而性能会降低,比如update方法,因为数据发生变更后,hibernate需要保持缓存和数据库两份的数据同步,所以加上缓存后,update性能降低,add、delete操作也是相同的道理。

所以缓存适用于在项目中存在大量查询的情况,否则是没必要适用的。


小结

在想项目之所以很吸引自己,很重要的一点是因为我们在使用各种各样的工具,包括在此提到的缓存、javamelody,正如“君子生非异也,善假于物也”,更多工具的使用,会在后面详细介绍。


作者:tcl_6666 发表于2014-4-10 20:54:30 原文链接
阅读:118 评论:2 查看评论

相关 [性能优化 hibernate 缓存] 推荐:

性能优化之Hibernate缓存讲解、应用和调优

- - CSDN博客系统运维推荐文章
    近来坤哥推荐我我们一款性能监控、调优工具——JavaMelody,通过它让我觉得项目优化是看得见摸得着的,优化有了针对性. 而无论是对于分布式,还是非分布,缓存是提示性能的有效工具.     数据层是EJB3.0实现的,而EJB3.0内部也是通过Hibernate实现的,而Hibernate本身提供了很好的缓存机制,我们只需要学会使用它驾驭它就够了.

Hibernate性能优化技巧

- - SQL - 编程语言 - ITeye博客
文章分为十三个小块儿对Hibernate性能优化技巧进行总结性分析,分析如下:. 一、在处理大数据量时,会有大量的数据缓冲保存在Session的一级缓存中,这缓存大太时会严重显示性能,所以在使用Hibernate处理大数据量的,可以使用session. clear()或者session. evict(Object) 在处理过程中,清除全部的缓存或者清除某个对象.

Hibernate 缓存

- - ITeye博客
1数据缓存:(date caching) 是一种将数据暂时存于内存缓存去中的技术,缓存通常是影响系统性能的关键因素. 2.ORM的数据缓存策略有3中.   1.事务级缓存:  分为 数据库事务和 应用级事务,是基于Session的生命周期的实现,每个session都会在内部维持一个数据缓存, 随session的创建和消亡.

Hibernate 二级缓存

- - CSDN博客推荐文章
很多人对二级缓存都不太了解,或者是有错误的认识,我一直想写一篇文章介绍一下hibernate的二级缓存的,今天终于忍不住了. 我的经验主要来自hibernate2.1版本,基本原理和3.0、3.1是一样的,请原谅我的顽固不化. hibernate的session提供了一级缓存,每个session,对同一个id进行两次load,不会发送两条sql给数据库,但是session关闭的时候,一级缓存就失效了.

Hibernate 缓存总结

- - 行业应用 - ITeye博客
1.         一级缓存只缓存整个对象,不能缓存对象属性;. 2.         一级缓存是Session级的缓存,不能跨多个Session对象来使用;. 3.         Session的load/get方法支持一级缓存的读和写;. 4.         Query的list接口只支持一级缓存的写入,不能从一级缓存中读出对象.

Hibernate的缓存机制

- - 企业架构 - ITeye博客
缓存是数据库数据在内存中的临时容器,它包含了库表数据在内存中的临时拷贝,位于数据库与应用程序之间,其作用是为了降低应用程序对物理数据源访问的频次,从而提高应用的运行性能. Hibernate的缓存机制. 1.1持久化层的缓存的范围.      持久层设计中,往往需要考虑几个不同层次中的数据缓存策略.

hibernate复习(1)性能优化之抓取策略

- - CSDN博客互联网推荐文章
抓取策略(fetching strategy) 是指:当应用程序需要在(Hibernate实体对象图的)关联关系间进行导航的时候, Hibernate如何获取关联对象的策略. 抓取策略可以在O/R映射的元数据中声明,也可以在特定的HQL 或条件查询(Criteria Query)中重载声明. 通过配置抓取策略可以直接影响Session的get()和load()方法的查询效率.

Spring/Hibernate 应用性能优化的7种方法

- - IT瘾-geek
【编者按】对于大多数典型的 Spring/Hibernate 企业应用而言,其性能表现几乎完全依赖于持久层的性能. 此篇文章中将介绍如何确认应用是否受数据库约束,同时介绍七种常用的提高应用性能的速成法:. 如何确认应用是否受限于数据库. 确认应用是否受限于数据库的第一步,是在开发环境中进行测试,并使用 VisualVM 进行监控.

hibernate缓存,一级缓存,二级缓存,查询缓存

- - CSDN博客推荐文章
1、缓存是数据库数据在内存中的临时容器,它包含了库表数据在内存中的临时拷贝,位于数据库和访问层之间. 2、ORM在进行数据读取时,会根据缓存管理策略,首先在缓冲中查询,如果发现,则直接使用,避免数据库调用的开销. 事务级缓存:当前事务范围内的数据缓存. 应用级缓存:某个应用中的数据缓存. 分布式缓存:多个应用,多个JVM之间共享缓存.

在Spring、Hibernate中使用Ehcache缓存

- - BlogJava-首页技术区
前一篇 http://www.blogjava.net/hoojo/archive/2012/07/12/382852.html介绍了Ehcache整合Spring缓存,使用页面、对象缓存;这里将介绍在Hibernate中使用查询缓存、一级缓存、二级缓存,整合Spring在HibernateTemplate中使用查询缓存.