缓存是数据库在内存中的临时容器,它包含了库表数据在内存中的临时拷贝,位于数据库和数据访问层中间。相对内存操作而言,数据库调用是一个代价高昂的过程,对于典型的企业级应用,数据库往往与应用服务器位于不同的物理服务器,这意味着每次数据访问都是一次远程调用,socket的创建于销毁,数据的打包拆包,数据库执行查询命令,网络传输上的必然延迟等等,这些消耗,方方面面的对我们系统的性能在进行影响。
此时,本地缓存的价值就尤为重要了,尤其是对于查询操作相对频繁的系统而言,如论坛系统,新闻系统等,良好的缓存机制以及合理的应用缓存,往往是提高性能的关键所在。
而在hibernate中,针对于可能发生的各种情况,它提供了一下三种缓存策略。1、事物级缓存 2、应用级/进程级缓存 3、分布式缓存
这里的事物可以是数据库事物。也能是某个应用级事物。对于hibernate而言,事物级别缓存是基于session的生命周期实现的,每个session会在内部维持一个数据缓存,此缓存随着session的创建而存在,销毁而消亡,也称session缓存,一级缓存或者内部缓存。
在某个应用或者某个独数据访问子集中的共享缓存。此缓存可以由多个事物(数据库事物或应用级事物)共享,事物之间的缓存共享策略与应用的事物隔离机制密切相关。在hibernate中,应用缓存在sessionfactory层实现,由它创建的session缓存实例共享此缓存,二级缓存,也叫sessionfactory级别缓存。在多实例并发运行时,我们必须特别小心此机制下,可能带来的负面效应,如实例a和b共享一个数据库,并行运行,a和b各自维护自己的缓存,如果缺乏同步机制,a对数据库做了更新,而b正在做读取,就会导致错误,这样的错误,对于关键业务是无法承受的,比如财务系统,银行系统等等。
在这种情况下,应用级缓存无法使用,为此,hibernate引入了分布式缓存。
在多个应用实例中,多个jvm之间共享的缓存模式。分布式缓存由多个应用级缓存实例组成集群,通过某种远程机制(RMI或JMS)实现各个缓存实例间的数据同步,任何实例的数据修改操作,都将导致整个集群间的数据状态同步。
分布式缓存解决了多并发实例并发运行的数据同步问题,但是除非对于并发读取性能要求较高,且读取操作子啊持久层中占绝大部分比重的情况,分布式缓存的实际效果还需要进一步考证。
因为多实例间的同步,每个缓存实例发生变动都会复制到其余所有节点中,这个开销也是很大的。
一级缓存常用于,1.通过id[主键]加载数据时 2.延时加载等等。由hibernate自动维护,无需手动干预,我们也可以通过调用session.evict[将某个特性对象从缓存中清除],和session.clear[清空内部缓存]。
二级缓存,是应用在一级中查不到的查不到的时候,才会在二级中去查找。在使用它时,我们必须考虑两点,第一,数据库是否于其他应用共享,第二,应用是否需要部署在集群中。假使出现第一种情况,那么我们不得不放弃二级的使用,假使出现第二种,则需要考虑清楚结合实际来使用。
那么当出现下面一些情况时,我们即可以使用二级缓存,1.数据不会被第三方应用修改 2.数据变动的大小在可接受范围内 3.数据更新频率较低 4.同一数据可能被系统频繁引用 5.非关键数据
基于java缓存实现,最简单的方式莫过于对集合数据类型进行封装。hibernate提供了基于hashtable的缓存实现机制,不过由于性能和功能上的局限,一般用于调试使用。同时,hibernate还提供了面向第三方缓存实现的接口,如:
1.JCS 2.EHCache 3.OSCahce 4.JBoss Cache 5.SwarmCache,而新版本的hibernate中则将EHCache作为其默认的二级缓存实现。由此,在hibernate中开启二级缓存,需要在hibernate.cfg.xml中进行一下配置,代码如下:
另外还需对cache本身进行配置,这里是ehcache的配置文件,ehcache.xml:
之后,在我们的映射文件里,指定就行了:
注意到上面我们对,<cache>节点的配置,hibernate提供了一下四种内置同步策略,
1.read-only:只读,对不会发生改变的数据,采用只读
2.nonstrict-read-write:并发访问下对同步要求不是和严格,且数据更新频率较低(几小时或几天)
3.read-write:严格可读写缓存,基于时间戳判定机制,虽不支持分布式缓存,但仍是应用中使用最多的同步策略
4.transactional:事物型缓存,必须运行在JTA事物环境中。