`

Hibernate缓存(转)

阅读更多
缓存可以简单的看成一个 Map ,通过 key 在缓存里面找 value 。

 

一、缓存简介   Cache In Hibernate

HIBERNATE 中的 CACHE 有两级 .

一级是在 Session 范围内的 CACHE . 即每个 Session 有自己的一个 CACHE, 当前操作的对象都会被保留在 CACHE 中 . 但是 Session 关闭后这个 CACHE 也就没有 . 可见这级 CACHE 的生命期是很短的 . (使用 id 进行关键字存储:缓存的 key 就是 ID , value 是 POJO ) ( 缓存的是实体对象 )

另一级 CACHE 是在 SessionFactory 范围的 , 可以被来自同一个 SessionFactory 的 Session 共享 . 在 HIBERNATE 的文档中称其为 SECOND LEVEL CACHE. 显然后者的优势较明显 , 也比较复合当前的使用环境 .    它可以使用不同的缓存实现,如 EhCache 、 JBossCache 、 OsCache 等 (二级缓存是缓存实体对象的)

还有一个类型的 CACHE 就是 QueryCache . 它的作用就是缓存一个 Query 以及 Query 返回对象的 Identifier 以及对象的类型 . 有了 QueryCache 后就可以高效的使用 SECOND LEVEL CACHE.

hibernate 查询缓存 (hibernate 默认是关闭的 )

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

对实体对象的结果集只缓存 id

查询缓存的生命周期,当前关联的表发生修改,那么查询缓存生命周期结束

查询缓存的配置和使用:

1. 启用查询缓存:在 hibernate .cfg.xml 中加入:

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

2. 在程序中必须手动启用查询缓存,如: query.setCacheable(true);

QueryCache 用来缓存查询语句 , 及查询结果集中对象的 Identifier 与 Type. 当再次使用已缓存的 Query 时 , 就可以通过对象的 Identifier 与 Type 在 SECOND LEVEL CACHE 中查找实际的对象 .

对于查询缓存来说,缓存的 key 是根据 hql 生成的 sql ,再加上参数,分页等信息(可以通过日志输出看到,不过它的输出不是很可读,最好改一下它的代码)。

 

 

注:一级缓存也叫 session 级的缓存或事务缓存。 Hibernate 二级缓存也称为进程级的缓存或 SessionFactory 级的缓存。二级缓存是全局缓存,它可以被所有的 session 共享。二级缓存的生命周期和 SessionFactory 的生命周期一致, SessionFactory 可以管理二级缓存。

 

 

二、缓存的范围

缓存的范围分为 3 类 :

1. 事务范围

      事务范围的缓存只能被当前事务访问 , 每个事务都有各自的缓存 , 缓存内的数据通常采用相互关联的对象形式 . 缓存的生命周期依赖于事务的生命周期 , 只有当事务结束时 , 缓存的生命周期才会结束 . 事务范围的缓存使用内存作为存储介质 , 一级缓存就属于事务范围 .

2. 应用范围

      应用程序的缓存可以被应用范围内的所有事务共享访问 . 缓存的生命周期依赖于应用的生命周期 , 只有当应用结束时 , 缓存的生命周期才会结束 . 应用范围的缓存可以使用内存或硬盘作为存储介质 , 二级缓存就属于应用范围 .

3. 集群范围

      在集群环境中 , 缓存被一个机器或多个机器的进程共享 , 缓存中的数据被复制到集群环境中的每个进程节点 , 进程间通过远程通信来保证缓存中的数据的一致 , 缓存中的数据通常采用对象的松散数据形式 .

三、缓存的方式

有四种,分别为:

   CacheConcurrencyStrategy.NONE

   CacheConcurrencyStrategy.READ_ONLY ,只读模式,在此模式下,如果对数据进行更新操作,会有异常;

   CacheConcurrencyStrategy.READ_WRITE ,读写模式在更新缓存的时候会把缓存里面的数据换成一个锁,其它事务如果去取相应的缓存数据,发现被锁了,直接就去数据库查询;

   CacheConcurrencyStrategy.NONSTRICT_READ_WRITE ,不严格的读写模式则不会的缓存数据加锁;

   CacheConcurrencyStrategy.TRANSACTIONAL ,事务模式指缓存支持事务,当事务回滚时,缓存也能回滚,只支持 JTA 环境。

 

缓存的注释写法如下,加在 Entity 的 java 类上:

   @Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)



四、缓存的管理

  Hibernate 的缓存管理

一级缓存的管理 :

  evit(Object obj)   将指定的持久化对象从一级缓存中清除 , 释放对象所占用的内存资源 , 指定对象从持久化状态变为脱管状态 , 从而成为游离对象 .

  clear()   将一级缓存中的所有持久化对象清除 , 释放其占用的内存资源

  contains(Object obj) 判断指定的对象是否存在于一级缓存中 .

  flush() 刷新一级缓存区的内容 , 使之与数据库数据保持同步 .

  二级缓存的管理 :   

   evict(Class arg0, Serializable arg1)   将某个类的指定 ID 的持久化对象从二级缓存中清除 , 释放对象所占用的资源 .

evictCollection(String arg0)   将指定类的所有持久化对象的指定集合从二级缓存中清除 , 释放其占用的内存资源 .

 

如何避免一次性大量的实体数据入库导致内存溢出

* 先 flush ,再 clear

如果数据量特别大,考虑采用 jdbc 实现,如果 jdbc 也不能满足要求,可以考虑采用数据库本身的特定导入工具

 

五、什么样的数据不适合放在二级缓存中来 ?

  下面这几种情况就不适合 加载到二级缓存中 :

  1. 经常被修改的数据

  2. 绝对不允许出现并发访问的数据

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

  下面这己种情况合适 加载到二级缓存中 :

  1. 数据更新频率低

  2. 允许偶尔出现并发问题的非重要数据

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

  4. 常量数据

  5. 不会被第三方修改的数据

 

六、二级缓存的配置

Hibernate 的二级缓存功能是靠配置二级缓存插件来实现的 ,Hibernate 为了集成这些插件 ,Hibernate 提供了 org.hibernate.cache.CacheProvider 借口 , 它充当缓存插件与 Hibernate 之间的适配器 .

常用的二级缓存插件

EHCache  org.hibernate.cache.EhCacheProvider

OSCache  org.hibernate.cache.OSCacheProvider

SwarmCahe  org.hibernate.cache.SwarmCacheProvider

JBossCache  org.hibernate.cache.TreeCacheProvider

 

设置 hibernate.cache.provider_class 。

我们这里用 ehcache ,如

hibernate.cache.provider_class=net.sf.hibernate.cache.EhCacheProvider

由于这是 HIBERNATE 默认的 CACHE 提供者 , 所以无须做什么设置 .

只要在 src 中添加 ehcache 的配置文件 ehcache.xml:

<ehcache>

    <diskStore path="java.io.tmpdir"/>  

  

    <defaultCache   maxElementsInMemory="10000"   eternal="false"  timeToIdleSeconds="120"          timeToLiveSeconds="120"  overflowToDisk="true"   />   

  

    <cache name="goncha.hb.bean.Person"   maxElementsInMemory="10"       eternal="false"    

        timeToIdleSeconds="100"  timeToLiveSeconds="100"  overflowToDisk="false"   />     

      <cache name="goncha.hb.bean.Address"  maxElementsInMemory="10"     eternal="false"    

        timeToIdleSeconds="100"  timeToLiveSeconds="100"  overflowToDisk="false"    />     

</ehcache>  

 

七、哪些方法支持缓存

*get()

*load()

*iterate()  ( 查询实体对象 )

save()

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

 

 

一级缓存测试:

1 . Load 测试 : 在同一个 session 中发出两次 load 查询( 1 )

2 . Get 测试 : 在同一个 session 中发出两次 get 查询( 1 )

3 . iterate 测试 : 在同一个 session 中发出两次 iterator 查询( 1 ( id ) +N , 1 ( id ))

4 . Iterate 查询属性测试 : 同一个 session 中发出两次查询属性( 2 次, iterate 查询普通属性,一级缓存不会缓存,所以会发出 sql )

5 .同一个 session 中先 save ,再发出 load 查询 save 过的数据 --save 是使用缓存的

6 .同一个 session 中先调用 load 查询,然后执行 sessio.clear() 或 session.evict() ,再调用 load 查询( 2 次)

       sessio.clear() 或 session.evict() 可以管理一级缓存,一级缓存无法取消,但可以管理 . 上面的语句都会发出 sql 因为一级缓存中的实体被清除了

7 .向数据库中批量加入 1000 条数据

       // 每一定条数据就强制 session 将数据持久化,同时清除缓存,避免大量数据造成内存溢出

 

开启二级缓存测试:

1 .开启两个 session 中发出两次 load 查询( get 与 load 一样, 1 次) ,

2 .开启两个 session ,分别调用 load ,再使用 sessionFactory 清楚二级缓存( 2 次)

3 .一级缓存和二级缓存的交互

       session.setCacheMode(CacheMode.GET);    // 设置成 只是从二级缓存里读 , 不向二级缓存里写数据 ( 2 )

       session.setCacheMode(CacheMode.PUT);  // 设置成只是向二级缓存里写数据,不读数据 ( 2 次)

 

开启 hibernate 查询缓存测试:

1 .   开启查询缓存,关闭二级缓存,开启一个 session ,分别调用 query.list  (查询属性)( 1 次)

2 .   开启查询缓存,关闭二级缓存,开启两个 session ,分别调用 query.list  (查询属性)

( 1 次)第二次没有去查询数据库,因为查询缓存生命周期与 session 生命周期无关

3 .   开启查询缓存,关闭二级缓存,开启两个 session ,分别调用 query.iterate (查询属性)

( 2 次)第二去查询数据库,因为查询缓存只对 query.list() 起作用,对 query.iterate() 不起作用 , 也就是说 query.iterate() 不使用查询缓存

4 .   关闭查询缓存,关闭二级缓存,开启两个 session ,分别调用 query.list (查询实体对象)       第二去查询数据库,因为 list 默认每次都会发出查询 sql

5 .   开启查询缓存,关闭二级缓存,开启两个 session ,分别调用 query.list (查询实体对象)       第二去查询数据库时,会发出 N 条 sql 语句,因为开启了查询缓存,关闭了二级缓存,那么查询缓存会缓存实体对象的 id ,所以 hibernate 会根据实体对象的 id 去查询相应的实体,如果缓存中不存在相应的实体,那么将发出根据实体 id 查询的 sql 语句,否则不会发出 sql ,使用缓存中的数据

6 .   开启查询缓存,开启二级缓存,开启两个 session ,分别调用 query.list (查询实体对象)       第二不会发出 sql ,因为开启了二级缓存和查询缓存,查询缓存缓存了实体对象的 id 列表, hibernate 会根据实体对象的 id 列表到二级缓存中取得相应的数据



八、二级缓存在项目中的集成和使用

1. 将 echcache.xml 文件拷贝到 src 下 , 二级缓存 hibernate 默认是关闭的,手动开启

2. 开启二级缓存,修改 hibernate .cfg.xml 文件 ,

<property name=”hibernate .cache.user_second_level_cache”>true</property>

3. 指定缓存产品提供商

<property name=”hibernate .cache.provider_calss”>org.hibernate .cache.EhCacheProvider</property>

4. 指定那些实体类使用二级缓存(两种方法,推荐使用第二种)

第一种:在 *.hbm.xml 中,在 <id> 之前加入

<cache usage=”read-only” />, 使用二级缓存

第二种:在 hibernate .cfg.xml 配置文件中 , 在 <mapping resource=”com/Studnet.hbm.xml” /> 后面 加上:

<class-cache class=” com.Studnet” usage=”read-only” />

 

总结:

不要想当然的以为缓存一定能提高性能,仅仅在你能够驾驭它并且条件合适的情况下才是这样的。 hibernate 的二级缓存限制还是比较多的,不方便用 jdbc 可能会大大的降低更新性能。在不了解原理的情况下乱用,可能会有 1+N 的问题。不当的使用还可能导致读出脏数据。

如果受不了 hibernate 的诸多限制,那么还是自己在应用程序的层面上做缓存吧。

在越高的层面上做缓存,效果就会越好 。就好像尽管磁盘有缓存,数据库还是要实现自己的缓存,尽管数据库有缓存,咱们的应用程序还是要做缓存。因为底层的缓存它并不知道高层要用这些数据干什么,只能做的比较通用,而高层可以有针对性的实现缓存,所以在更高的级别上做缓存,效果也要好些吧。

 

对于性能的看法是:合理的性能而不是最高的性能。什么是合理的?用户能感觉到(或敏感的)的范围内,不成为物理上业务处理的瓶颈,比如常说的 3 秒之内响应等。

[/size][/size][/size]
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics