Skip to content

Latest commit

 

History

History
81 lines (45 loc) · 4.19 KB

缓存.md

File metadata and controls

81 lines (45 loc) · 4.19 KB

概念

与Session相对的是,SessionFactory也提供了相应的缓存机制。SessionFactory缓存可以依据功能和目的的不同而划分为内置缓存和外置缓存。

SessionFactory的内置缓存中存放了映射元数据和预定义SQL语句,映射元数据是映射文件中数据的副本,而预定义SQL语句是在 Hibernate初始化阶段根据映射元数据推导出来的。SessionFactory的内置缓存是只读的,应用程序不能修改缓存中的映射元数据和预定义 SQL语句,因此SessionFactory不需要进行内置缓存与映射文件的同步。

SessionFactory的外置缓存是一个可配置的插件。在默认情况下,SessionFactory不会启用这个插件。外置缓存的数据是数据库数据 的副本,外置缓存的介质可以是内存或者硬盘。SessionFactory的外置缓存也被称为Hibernate的二级缓存。

Hibernate的二级缓存的实现原理与一级缓存是一样的,也是通过以ID为key的Map来实现对对象的缓存。

由于Hibernate的二级缓存是作用在SessionFactory范围内的,因而它比一级缓存的范围更广,可以被所有的Session对象所共享。

工作内容: ● 在执行各种条件查询时,如果所获得的结果集为实体对象的集合,那么就会把所有的数据对象根据ID放入到二级缓存中。

● 当Hibernate根据ID访问数据对象的时候,首先会从Session一级缓存中查找,如果查不到并且配置了二级缓存,那么会从二级缓存中查找,如果还查不到,就会查询数据库,把结果按照ID放入到缓存中。

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

使用场景

   在通常情况下会将具有以下特征的数据放入到二级缓存中:

● 很少被修改的数据。

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

● 不会被并发访问的数据。

● 参考数据。

   而对于具有以下特征的数据则不适合放在二级缓存中:

● 经常被修改的数据。

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

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

缓存策略

只读缓存(read-only):没有什么好说的

读/写缓存(read-write):程序可能要的更新数据

不严格的读/写缓存(nonstrict-read-write):需要更新数据,但是两个事务更新同一条记录的可能性很小,性能比读写缓存好

事务缓存(transactional):缓存支持事务,发生异常的时候,缓存也能够回滚,只支持jta环境

使用注意

使用二级缓存的前置条件

你的hibernate程序对数据库有独占的写访问权,其他的进程更新了数据库,hibernate是不可能知道的。

N+1问题

什么时候会遇到1+N的问题? 前提:Hibernate默认表与表的关联方法是fetch="select",不是fetch="join",这都是为了懒加载而准备的

1)一对多(),在1的这方,通过1条sql查找得到了1个对象,由于关联的存在,那么又需要将这个对象关联的集合取出,所以合集数量是n还要发出n条sql,于是本来的1条sql查询变成了1+n条。

2)多对一,在多的这方,通过1条sql 查询得到了n个对象,由于关联的存在,也会将这n个对象对应的1方的对象取出,于是本来的1条sql查询变成了1+n条

3)iterator查询时,一定先去缓存中找(1条sql查集合,只查出ID),在没命中时,会再按ID到库中逐一查找,产生1+n条SQL

怎么解决1+N问题? 1)lazy=true,hibernate3开始已经默认是lazy=true了;lazy=true时不会立刻查询关联对象,只有当需要关联对象(访问其属性,非id字段)时才会发生查询动作。

2)使用二级缓存,二级缓存的应用将不怕1+N问题,因为即使第一次查询很慢(未命中),以后查询直接缓存命中也是很快的。刚好又利用了1+N

3)当然你也可以设定fetch="join",一次关联表全查出来,但失去了懒加载的特性。

如果是list方式的话,value在这里并不是整个结果集,而是查询出来的这一串ID。也就是说,不管是list方法还是iterate方法,第一次查询的时候,它们的查询方式很它们平时的方式是一样的,list执行一条sql,iterate执行1+N条,多出来的行为是它们填充了缓存。但是到同样条件第二次查询的时候,就都和iterate的行为一样了,根据缓存的key去缓存里面查到了value,value是一串id,然后在到class的缓存里面去一个一个的load出来。这样做是为了节约内存。 可以看出来,查询缓存需要打开相关类的class缓存。list和iterate方法第一次执行的时候,都是既填充查询缓存又填充class缓存的。

这里还有一个很容易被忽视的重要问题,即打开查询缓存以后,即使是list方法也可能遇到1+N的问题!相同条件第一次list的时候,因为查询缓存中找不到,不管class缓存是否存在数据,总是发送一条sql语句到数据库获取全部数据,然后填充查询缓存和class缓存。但是第二次执行的时候,问题就来了,如果你的class缓存的超时时间比较短,现在class缓存都超时了,但是查询缓存还在,那么list方法在获取id串以后,将会一个一个去数据库load!因此,class缓存的超时时间一定不能短于查询缓存设置的超时时间!如果还设置了发呆时间的话,保证class缓存的发呆时间也大于查询的缓存的生存时间。这里还有其他情况,比如class缓存被程序强制evict了,这种情况就请自己注意了。

使用

● 在Hibernate配置文件(通常为hibernate.cfg.xml)中,设置二级缓存的提供者类。

● 配置EHCache的基本参数。

● 在需要进行缓存的实体对象的映射文件中配置缓存的策略。