Hibernate缓存机制剖析

Hibernate是基于缓存机制实现的。Hibernate的缓存包括:一级缓存、二级缓存和查询缓存。

Hibernate中支持懒加载load,也支持及时加载get。Hibernate采用CGlib的动态代理实现延迟加载。延迟加载采用CGlib的Enhancer类动态生成类。

比较

下面对Hibernate中一级缓存、二级缓存、查询缓存机制做一个横向比较:

相同点:

1、均为缓存,均可在一定的条件下缓存数据;

2、Hibernate的查询实现,是基于缓存机制;

3、三种缓存方式的内部实现方式类似,均使用key-value的map键值对方式实现;

4、一级缓存与二级缓存,军师对实体对象的缓存。内部实现均是:key里面放的是对象主键Id,value里面放的是实体对象。所以它们是实体对象的缓存。

不同点:

1、一级缓存,又称Session缓存,或者事务级缓存,它是内置的,不能被卸载。由于Session对象的生命周期通常对应一个数据库事务或一个应用事务,所以它的缓存是事务范围的缓存;

2、二级缓存,又称为SessionFactory缓存,它是进程范围或者集群范围的缓存,有可能出现并发问题,因此需要采用适当的并发策略,该策略为被缓存的数据提供了事务隔离级别;

3、查询缓存,是普通属性的缓存。查询缓存的生命周期:当关联的表发生修改,查询缓存的生命周期结束;

4、一级缓存是进程级的,进程结束后,即Session关闭后,缓存也随之清空;二级缓存与查询缓存均与Session无关,二级缓存的生命周期可以根据策略手动配置;查询缓存的生命周期与相关联的表相关;

5、一级缓存的存活期比较短,所以命中率比较低;查询缓存当关联表数据变更时,缓存生命周期结束,故命中率也比较低;二级缓存存活期较长,实际项目中应用也比较多;

6、一级缓存并不是为了大幅度提高性能而设置的,Hibernate主要使用一级缓存进行数据同步;而二级缓存的使用,由于它是进程范围内的缓存,可以大幅度提高性能。而查询缓存是为了缓存普通属性设置的缓存。

一级缓存

各种举例

拿公司和员工来举例子:

一、同一个session中,发生两次Load查询

在同一个session中,发出两次load查询。第一次查询,locad时查询代理,不查询上来数据,执行方法时,执行语句;

第二次查询,将不会再发出sql语句,直接在一级缓存里面取得。

二、同一个session中,发生两次get查询

在同一个session中,发出两次get查询。第一次查询,get时查询,马上执行查询语句,查询上来数据,方法执行时不再发sql语句。同时,将该数据放入缓存中。

第二次查询时,不会再发出sql语句,直接从缓存里面取得,除非第二次查询时,数据发生了变化。它与load一样,缓存中有的话,将直接查询。

三、同一个session中,发出两次iterate查询,查询实体对象

同一个session中,发出两次iterate查询,查询实体对象

第一次查询,发生N+1问题。第一次是查询所有id,然后去缓存里面去找,因为是第一次查询,缓存里面没有数据;所以会根据id,去数据库里面再次查询n次。

第二次查询,它会发出查询id语句,然后直接去缓存里面取得。

四、同一个session中,发出两次iterate查询,查询普通属性

同一个session中,发出两次iterate查询,查询普通属性。

这时,它还是会把sql语句发出来。

这是因为,iterate查询普通属性,一级缓存不会缓存,一级缓存只缓存实体对象查询。普通属性查询,不会缓存。

一级缓存是缓存实体对象的

五、在两个session里面,发load查询

在两个session里面,发load查询

由于session是进程级缓存,一个线程对应一个session,所以第一次查询后,会关闭session

第二次再次开启session

session间不能缓存一级缓存数据。因为它会伴随着session的消亡,而消亡。

所以会发两条语句

六、在同一个session中,先调用save,在调用load查询刚刚save的数据

在同一个session中,先调用save,在调用load查询刚刚save的数据

save也是支持缓存的。save之后,它会先往缓存里面存一份,取得时候,直接在缓存里面取

查询时,不会发出sql语句,因为save支持缓存。

七、大批量数据添加

大批量数据添加

对于大批量的数据添加,我们采用下面的做法:

每二十条数据,清一下缓存,到数据库里面存一次。最后提交事务

二级缓存

二级缓存,是SessionFactory级别的,因为SessionFactory能够管理它。二级缓存可以被所有的session共享。二级缓存,默认不启动,一般需要使用第三方产品。

使用ehcache第三方产品支持Hibernate的缓存。

<ehcache>
    <diskStore path="java.io.tmpdir"/>  <!--超出部分保存的位置-->

    <defaultCache
        maxElementsInMemory="10000"   <!--缺省配置,可以防止一万个对象-->
        eternal="false"      <!--过不过期,true时,永远不过期-->
        timeToIdleSeconds="120"  <!--第一次访问后,间隔120秒没被访问,就清掉缓存-->
        timeToLiveSeconds="120"   <!--缓存能够存活的时间120秒-->
        overflowToDisk="true"  <!--溢出的问题,如果已经超出了1万个对象,设置为TRUE,就保存在磁盘上-->
        />

</ehcache>

一、开启二级缓存,开启两个session,执行两次load方法

第一次load使用时,执行sql语句;第二次执行load时,不发语句。

因为它先到一级缓存里面找,没有数据,然后再去二级缓存里面找,查找到数据。

session可以共享二级缓存中的数据;二级缓存是进程级的。

二、开启二级缓存,开启两个session,执行两次get方法

与load基本类似,第一次查询发语句,第二次查询查询二级缓存。

三、开启二级缓存,在两个session中发load查询,采用SessionFactory管理二级缓存

//删除二级缓存

//HibernateUtils.getSessionFactory().evict(Student.class);

HibernateUtils.getSessionFactory().evict(Student.class, 1);

第二次查询时:会发出查询语句,因为二级缓存中的数据被清除了

四、开启二级缓存 ,一级缓存和二级缓存的交互

//禁止将一级缓存中的数据放到二级缓存中

session.setCacheMode(CacheMode.IGNORE);

第二次查询时:会发出查询语句,因为禁止了一级缓存和二级缓存的交互

五、 大批量的数据添加

大批量数据交互时,禁止一级缓存与二级缓存的交互,同时每二十条清一下一级缓存。

总结:二级缓存与一级缓存一模一样,只缓存实体对象,不缓存普通属性。

查询缓存

查询缓存是缓存普通属性的结果集

对实体对象的结果集缓存会缓存Id,生命周期不定。当表里的数据发生变化,查询缓存的生命周期结束。

查询缓存的配置和使用:

修改hibernate.cfg.xml文件,来开启查询缓存,默认是false,是不起用的

<property name="hibernate.cache.use_query_cache">true</property>

//必须在程序启用

query.setCacheable(true)

一、开启查询,关闭二级缓存,采用query.list()查询普通属性

在一个session中发query.list()查询

//执行两次如下代码

List names = session.createQuery("select s.name from Student s")

.setCacheable(true)

.list();

第一次将发出sql语句,第二次不发语句

二、 开启查询,关闭二级缓存,采用query.list()查询普通属性

在两个session中发query.list()查询

执行结果如上,跨session。查询缓存与session没关系

三、开启查询,关闭二级缓存,采用query.iterate()查询普通属性

在两个session中发query.iterate()查询

第二次查询会发出sql语句,

会发出查询语句,query.iterate()查询普通属性它不会使用查询缓存

查询缓存只对query.list()起作用

四、关闭查询,关闭二级缓存,采用query.list()查询实体

在两个session中发query.list()查询

会发出查询语句,默认query.list()每次执行都会发出查询语句

五、开启查询,关闭二级缓存,采用query.list()查询实体在两个session中发query.list()查询

会发出n条查询语句,因为开启了查询缓存,关闭了二级缓存,那么查询缓存就会缓存实体对象的id

第二次执行query.list(),将查询缓存中的id依次取出,分别到一级缓存和二级缓存中查询相应的实体

对象,如果存在就使用缓存中的实体对象,否则根据id发出查询学生的语句

六、开启查询,开启二级缓存,采用query.list()查询实体

在两个session中发query.list()查询

不再发出查询语句,因为配置了二级缓存和查询缓存

其他

1、加载方式

Hibernate有四种加载方式:即时加载、延迟加载、预先加载和批量加载。

a、即时加载

实体加载完成后,立即加载与实体先关连的数据,并填充到实体对应的属性中。这种加载方式通常会发多条sql语句;

b、延迟加载

实体加载时,其关联数据并不是立即读取,而是当关联数据第一次被访问再进行读取。这种加载方式在第一次访问关联数据时,必须在同一个session中,否则包session关闭的错误;

这种方式内部是采用动态代理实现的,具体内容请参见Spring AOP一文

c、预先加载

预先加载是发出“outer-join”语句。可以通过全局变量Hibernate.max_fetch_depth限定join的层次

d、批量加载

对于及时加载和延迟加载,可以采用批量价在进行优化。

批量价在通过批量提交多个限制条件,一次多个限定条件的数据读取。同时在实体映射文件中的class节点,通过配置batch-size参数打开批量加载机制,并限定每次批量加载数据的数量。

2、缓存机制导致的问题

Hibernate的缓存机制,导致了一些问题:a、n+1问题;b、OpenSessionInView

a、n+1问题

n+1问题就是在迭代查询时,发出了n+1条语句,如:

Iterator iter = session.createQuery("from User").list();

执行上面这段代码,直接返回一个迭代器。这时,假设一共有n条数据,那么它会执行n+1条sql语句。

这是因为:首先,它会把所有的主键Id查询上来,然后根据Id,去缓存里面找,也就是session里面找。如果缓存中已经有的数据的话,则它会直接从缓存里面取;如果缓存里面没有数据,则它需要根据Id,一条一条去数据库里面查询。所以会发出n+1条语句。

如何避免n+1问题?

可以先返回List:

List users = session.createQuery("from User").list();

该查询,会将查询上来的数据保存在缓存里面,也就是一级缓存里面。这是我们不关闭session,使用同一个session,进行迭代查询。

Iterator iter = session.createQuery("from User").list();

这时,它会发送一条查询Id的sql语句,然后再去一级缓存里面找,因为缓存里面都有数据,所以会直接查询上来。

注意:List查询时,不查询缓存;Iterator查询,会查询缓存。

b、OpenSessionInView

Hibernate查询中采用懒加载时,即设置了lazy=true,那么读取数据的时候,当读取了父数据后,进程结束。Hibernate会自动关系Session,这样,当要使用子数据的时候,系统会找不到当前session,所以,这就需要保持session一直开着,直到调用调用结束为止。

这个好办,只需要在发出请求之初,开启session时,将开启的session放入Threadlocal中,结束后再在Threadlocal中结束该进程。这个过程就是OpensessionInView的过程。

总结

1、一级缓存与二级缓存的最大区别就是:一级缓存是Session级缓存;二级缓存是跨Session的缓存。其他机制完全类似。

2、二级缓存是Session间的,能够跨Session;

3、查询缓存是Session间的,能够跨Session;

4、query.iterate()查询普通属性不会使用查询缓存,查询缓存只对query.list()起作用;

5、一级缓存生命周期很短暂,故命中率不高;查询缓存的生命周期与相关联表有关。关联表数据有变动,查询缓存的生命周期结束;二级缓存的生命周期可以认为控制,利用率较大。

时间: 2024-10-19 01:34:17

Hibernate缓存机制剖析的相关文章

10.hibernate缓存机制详细分析(转自xiaoluo501395377)

hibernate缓存机制详细分析 在本篇随笔里将会分析一下hibernate的缓存机制,包括一级缓存(session级别).二级缓存(sessionFactory级别)以及查询缓存,当然还要讨论下我们的N+1的问题. 随笔虽长,但我相信看完的朋友绝对能对hibernate的 N+1问题以及缓存有更深的了解. 一.N+1问题 首先我们来探讨一下N+1的问题,我们先通过一个例子来看一下,什么是N+1问题: list()获得对象: 1 /** 2 * 此时会发出一条sql,将30个学生全部查询出来

[转]hibernate缓存机制所有详解

以下文章来自http://www.blogjava.net/tbwshc/articles/380013.html Hibernate 所有缓存机制详解 hibernate提供的一级缓存 hibernate是一个线程对应一个session,一个线程可以看成一个用户.也就是说session级缓存(一级缓存)只能给一个线程用,别的线程用不了,一级缓存就是和线程绑定了. hibernate一级缓存生命周期很短,和session生命周期一样,一级缓存也称session级的缓存或事务级缓存.如果tb事务提

浅谈Hibernate缓存机制:一级缓存、二级缓存

一:什么是缓存机制 当我们频繁访问数据库时,尤其像Hibernate持久层框架,会导致数据库访问性能降低,因此我们期望有一种机制能提供一个"缓存空间",我们将需要的数据复制到这个"缓存空间",当数据查询时,我们先在这个"缓存空间"里找,如果没有,我们再去数据库查找,这样就减少了与数据库的访问,从而提高了数据库访问性能,这就是缓存机制. 二:Hibernate缓存机制 1:一级缓存:Hibernate默认的缓存机制,它属于Session级别的缓存机

Hibernate 缓存机制

一.why(为什么要用Hibernate缓存?) Hibernate是一个持久层框架,经常访问物理数据库. 为了降低应用程序对物理数据源访问的频次,从而提高应用程序的运行性能. 缓存内的数据是对物理数据源中的数据的复制,应用程序在运行时从缓存读写数据,在特定的时刻或事件会同步缓存和物理数据源的数据. 二.what(Hibernate缓存原理是怎样的?)Hibernate缓存包括两大类:Hibernate一级缓存和Hibernate二级缓存. 1.Hibernate一级缓存又称为“Session的

hibernate缓存机制详细分析 复制代码

您可以通过点击 右下角 的按钮 来对文章内容作出评价, 也可以通过左下方的 关注按钮 来关注我的博客的最新动态. 如果文章内容对您有帮助, 不要忘记点击右下角的 推荐按钮 来支持一下哦 如果您对文章内容有任何疑问, 可以通过评论或发邮件的方式联系我: [email protected] / [email protected] 如果需要转载,请注明出处,谢谢!! 在本篇随笔里将会分析一下hibernate的缓存机制,包括一级缓存(session级别).二级缓存(sessionFactory级别)以

面试中对Hibernate缓存机制的回答

这是面试中经常问到的一个问题,可以按照下面的思路回答,准你回答得很完美.首先说下Hibernate缓存的作用(即为什么要用缓存机制),然后再具体说说Hibernate中缓存的分类情况,最后可以举个具体的例子.Hibernate缓存的作用: Hibernate是一个持久层框架,经常访问物理数据库,为了降低应用程序对物理数据源访问的频次,从而提高应用程序的运行性能.缓存内的数据是对物理数据源中的数据的复制,应用程序在运行时从缓存读写数据,在特定的时刻或事件会同步缓存和物理数据源的数据Hibernat

Hibernate 缓存机制详细解析

一.why(为什么要用Hibernate缓存?) Hibernate是一个持久层框架,经常访问物理数据库. 为了降低应用程序对物理数据源访问的频次,从而提高应用程序的运行性能. 缓存内的数据是对物理数据源中的数据的复制,应用程序在运行时从缓存读写数据,在特定的时刻或事件会同步缓存和物理数据源的数据. 二.what(Hibernate缓存原理是怎样的?)Hibernate缓存包括两大类:Hibernate一级缓存和Hibernate二级缓存. 1.Hibernate一级缓存又称为“Session的

Hibernate 缓存机制(转)

一.why(为什么要用Hibernate缓存?) Hibernate是一个持久层框架,经常访问物理数据库. 为了降低应用程序对物理数据源访问的频次,从而提高应用程序的运行性能. 缓存内的数据是对物理数据源中的数据的复制,应用程序在运行时从缓存读写数据,在特定的时刻或事件会同步缓存和物理数据源的数据. 二.what(Hibernate缓存原理是怎样的?)Hibernate缓存包括两大类:Hibernate一级缓存和Hibernate二级缓存. 1.Hibernate一级缓存又称为“Session的

hibernate缓存机制和事务隔离机制

一级缓存( Session缓存) }         一级缓存的管理 ?          应用程序调用Session的save().update().saveOrUpdate().get()或load(),以及调用查询接口的 list().iterate() 时,如果在Session缓存中还不存在相应的对象,Hibernate就会把该对象加入到第一级缓存中. ?          可以通过close/clear/evict清空缓存 }         作用 因为Session的生命期往往很短,