Hibernate缓存原理:

对于Hibernate这类ORM而言,缓存显的尤为重要,它是持久层性能提升的关键.简单来讲Hibernate就是对JDBC进行封装,以实现内部状态的管理,OR关系的映射等,但随之带来的就是数据访问效率的降低,和性能的下降,而缓存就是弥补这一缺点的重要方法.

缓存就是数据库数据在内存中的临时容器,包括数据库数据在内存中的临时拷贝,它位于数据库与数据库访问层中间.ORM在查询数据时首先会根据自身的缓存管理策略,在缓存中查找相关数据,如发现所需的数据,则直接将此数据作为结果加以利用,从而避免了数据库调用性能的开销.而相对内存操作而言,数据库调用是一个代价高昂的过程.
     一般来讲ORM中的缓存分为以下几类:
         1:事务级缓存:即在当前事务范围内的数据缓存.就Hibernate来讲,事务级缓存是基于Session的生命周期实现的,每个Session内部会存在一个数据缓存,它随着 Session的创建而存在,随着Session的销毁而灭亡,因此也称为Session Level Cache.
         2:应用级缓存:即在某个应用中或应用中某个独立数据库访问子集中的共享缓存,此缓存可由多个事务共享(数据库事务或应用事务),事务之间的缓存共享策略与应用的事务隔离机制密切相关.在Hibernate中,应用级缓存由SessionFactory实现,所有由一个SessionFactory创建的 Session实例共享此缓存,因此也称为SessionFactory Level Cache.
         3:分布式缓存:即在多个应用实例,多个JVM间共享的缓存策略.分布式缓存由多个应用级缓存实例组成,通过某种远程机制(RMI,JMS)实现各个缓存实例间的数据同步,任何一个实例的数据修改,将导致整个集群间的数据状态同步.

Hibernate的一,二级缓存策略:

Hibernate中提供了两级Cache,第一级别的缓存是Session级别的缓存,它是属于事务范围的缓存。这一级别的缓存由hibernate管理的,一般情况下无需进行干预;第二级别的缓存是SessionFactory级别的缓存,它是属于进程范围或群集范围的缓存。这一级别的缓存可以进行配置和更改,并且可以动态加载和卸载,属于多事务级别,要防止事务并发性。

缓存是以map的形式进行存储的(key-id,value-object)

一级缓存(Session):

事务范围,每个事务(Session)都有单独的第一级缓存.

一级缓存的管理:当应用程序调用Session的save()、update()、saveOrUpdate()、get()或load(),以及调用查询接口的 list()、iterate()--(用的是n+1次查询,先查id)或filter()方法时,如果在Session缓存中还不存在相应的对象,Hibernate就会把该对象加入到第一级缓存中。当清理缓存时,Hibernate会根据缓存中对象的状态变化来同步更新数据库。 Session为应用程序提供了两个管理缓存的方法: evict(Object obj):从缓存中清除参数指定的持久化对象。 clear():清空缓存中所有持久化对象,flush():使缓存与数据库同步。

当查询相应的字段如(name),而不是对象时,不支持缓存。

二级缓存(SessionFactory):

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

1:条件查询的时候,总是发出一条select * from table_name where …. (选择所有字段)这样的SQL句查询数据库,一次获得所有的数据对象(这个问题要考虑,如果你查询十万条数据时,内存不是被占用)。

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

 3: 当Hibernate根据ID访问数据对象的时候,首先从Session一级缓存中查;查不到,如果配置了二级缓存,那么从二级缓存中查;查不到,再查询数据库,把结果按照ID放入到缓存。

4:删除、更新、增加数据的时候,同时更新缓存。

Hibernate的二级缓存策略,是针对于ID查询的缓存策略,对于条件查询则毫无作用。为此,Hibernate提供了针对条件查询的Query Cache

概念讲解

在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操作也是相同的道理。

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

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

1.很少被修改的数据

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

3.不会被并发访问的数据

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

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

1 经常被修改的数据

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

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

 常用的缓存插件 Hibernater 的二级缓存是一个插件,下面是几种常用的缓存插件:

  EhCache:可作为进程范围的缓存,存放数据的物理介质可以是内存或硬盘,对Hibernate的查询缓存提供了支持。

  OSCache:可作为进程范围的缓存,存放数据的物理介质可以是内存或硬盘,提供了丰富的缓存数据过期策略,对Hibernate的查询缓存提供了支持。

  SwarmCache:可作为群集范围内的缓存,但不支持Hibernate的查询缓存。

  JBossCache:可作为群集范围内的缓存,支持事务型并发访问策略,对Hibernate的查询缓存提供了支持。

配置二级缓存的主要步骤:

  1 选择需要使用二级缓存的持久化类,设置它的命名缓存的并发访问策略。这是最值得认真考虑的步骤。

  2 选择合适的缓存插件,然后编辑该插件的配置文件。

Hibernate缓存原理与策略的更多相关文章

  1. Hibernate缓存原理与策略 Hibernate缓存原理:

    Hibernate缓存原理: 对于Hibernate这类ORM而言,缓存显的尤为重要,它是持久层性能提升的关键.简单来讲Hibernate就是对JDBC进行封装,以实现内部状态的管理,OR关系的映射等 ...

  2. Hibernate缓存原理

    对于Hibernate这类ORM而言,缓存显的尤为重要,它是持久层性能提升的关键. 简单来讲Hibernate就是对JDBC进行封装,以实现内部状态的管理,OR关系的映射等, 但随之带来的就是数据访问 ...

  3. Hibernate 缓存机制浅析

    1. 为什么要用 Hibernate 缓存? Hibernate是一个持久层框架,经常访问物理数据库. 为了降低应用程序对物理数据源访问的频次,从而提高应用程序的运行性能. 缓存内的数据是对物理数据源 ...

  4. hibernate缓存机制(转)

    原文出处:http://www.cnblogs.com/wean/archive/2012/05/16/2502724.html 一.why(为什么要用Hibernate缓存?) Hibernate是 ...

  5. Hibernate缓存(转)

    来自:http://www.cnblogs.com/wean/archive/2012/05/16/2502724.html 一.why(为什么要用Hibernate缓存?) Hibernate是一个 ...

  6. Hibernate 缓存机制

    一.why(为什么要用Hibernate缓存?) Hibernate是一个持久层框架,经常访问物理数据库. 为了降低应用程序对物理数据源访问的频次,从而提高应用程序的运行性能. 缓存内的数据是对物理数 ...

  7. Hibernate 缓存机制二(转)

    感谢:http://www.cnblogs.com/wean/archive/2012/05/16/2502724.html 一.why(为什么要用Hibernate缓存?) Hibernate是一个 ...

  8. Hibernate 缓存机制(转)

    一.why(为什么要用Hibernate缓存?) Hibernate是一个持久层框架,经常访问物理数据库. 为了降低应用程序对物理数据源访问的频次,从而提高应用程序的运行性能. 缓存内的数据是对物理数 ...

  9. hibernate缓存机制(二级缓存)

    一.why(为什么要用Hibernate缓存?) Hibernate是一个持久层框架,经常访问物理数据库. 为了降低应用程序对物理数据源访问的频次,从而提高应用程序的运行性能. 缓存内的数据是对物理数 ...

随机推荐

  1. IStream 接口

    IStream 接口  https://msdn.microsoft.com/en-us/library/windows/apps/aa380034 IStream接口允许您读取和写入 stream ...

  2. 【原创】Kakfa common包源代码分析

    初一看common包的代码吓了一跳,这么多scala文件!后面仔细一看大部分都是Kafka自定义的Exception类,简直可以改称为kafka.exceptions包了.由于那些异常类的名称通常都定 ...

  3. 【转载】8天学通MongoDB——第四天 索引操作

    这些天项目改版,时间比较紧,博客也就没跟得上,还望大家见谅. 好,今天分享下mongodb中关于索引的基本操作,我们日常做开发都避免不了要对程序进行性能优化,而程序的操作无非就是CURD,通常我们 又 ...

  4. ASP.NET Core 发布至Linux生产环境 Ubuntu 系统

    ASP.NET Core 发布至Linux生产环境 Ubuntu 系统,之前跟大家讲解了 dotnet publish 发布,而没有将整个系统串起来. 今天就跟大家综合的讲一下ASP.NET Core ...

  5. .NET Core 单元测试 MSTest

    .NET Core 单元测试 MSTest ,MSTest Framework 已经支持 .NET Core RC2 / ASP.NET Core RC2. 之前都是使用 xUnit.net ,现在 ...

  6. SqlServer中的自增的ID的最后的值:

    SqlServer中的自增的ID的最后的值: SELECT SCOPE_IDENTITY() --返回插入到同一作用域中的 IDENTITY 列内的最后一个 IDENTITY 值.SELECT @@I ...

  7. Lucene.net站内搜索—5、搜索引擎第一版实现

    目录 Lucene.net站内搜索—1.SEO优化 Lucene.net站内搜索—2.Lucene.Net简介和分词Lucene.net站内搜索—3.最简单搜索引擎代码Lucene.net站内搜索—4 ...

  8. Python多线程学习

    一.Python中的线程使用: Python中使用线程有两种方式:函数或者用类来包装线程对象. 1.  函数式:调用thread模块中的start_new_thread()函数来产生新线程.如下例: ...

  9. GJM :用JIRA管理你的项目(三)基于LDAP用户管理 [转载]

    感谢您的阅读.喜欢的.有用的就请大哥大嫂们高抬贵手"推荐一下"吧!你的精神支持是博主强大的写作动力以及转载收藏动力.欢迎转载! 版权声明:本文原创发表于 [请点击连接前往] ,未经 ...

  10. C#微信公众平台开发者模式开启代码

    using System;using System.IO;using System.Text;using System.Web.Security; namespace HPZJ.Web.sys.exc ...