hibernate缓存机制(转载)
缓存是介于应用程序和物理数据源之间,其作用是为了降低应用程序对物理数据源访问的频次,从而提高了应用的运行性能。缓存内的数据是对物理数据源中的数据的复制,应用程序在运行时从缓存读写数据,在特定的时刻或事件会同步缓存和物理数据源的数据。
基本信息
缓存的介质一般是内存,所以读写速度很快。但如果缓存中存放的数据量非常大时,也会用硬盘作为缓存介质。缓存的实现不仅仅要考虑存储的介质,还要考虑到管理缓存的并发访问和缓存数据的生命周期。
Hibernate的缓存包括Session的缓存和SessionFactory的缓存,其中SessionFactory的缓存又可以分为两类:内置缓存和外置缓存。Session的缓存是内置的,不能被卸载,也被称为Hibernate的第一级缓存。SessionFactory的内置缓存和Session的缓存在实现方式上比较相似,前者是SessionFactory对象的一些集合属性包含的数据,后者是指Session的一些集合属性包含的数据。SessionFactory的内置缓存中存放了映射元数据和预定义SQL语句,映射元数据是映射文件中数据的拷贝,而预定义SQL语句是在Hibernate初始化阶段根据映射元数据推导出来,SessionFactory的内置缓存是只读的,应用程序不能修改缓存中的映射元数据和预定义SQL语句,因此SessionFactory不需要进行内置缓存与映射文件的同步。SessionFactory的外置缓存是一个可配置的插件。在默认情况下,SessionFactory不会启用这个插件。外置缓存的数据是数据库数据的拷贝,外置缓存的介质可以是内存或者硬盘。SessionFactory的外置缓存也被称为Hibernate的第二级缓存。
Hibernate的这两级缓存都位于持久化层,存放的都是数据库数据的拷贝,那么它们之间的区别是什么呢?为了理解二者的区别,需要深入理解持久化层的缓存的两个特性:缓存的范围和缓存的并发访问策略。
什么样的数据适合存放到第二级缓存中?
1 很少被修改的数据
2 不是很重要的数据,允许出现偶尔并发的数据
3 不会被并发访问的数据
4 参考数据
不适合存放到第二级缓存的数据?
1 经常被修改的数据
2 财务数据,绝对不允许出现并发
3 与其他应用共享的数据。
Hibernate的二级缓存
如前所述,Hibernate提供了两级缓存,第一级是Session的缓存。由于Session对象的生命周期通常对应一个数据库事务或者一个应用事务,因此它的缓存是事务范围的缓存。第一级缓存是必需的,不允许而且事实上也无法卸除。在第一级缓存中,持久化类的每个实例都具有唯一的OID。
第二级缓存是一个可插拔的的缓存插件,它是由SessionFactory负责管理。由于SessionFactory对象的生命周期和应用程序的整个过程对应,因此第二级缓存是进程范围或者集群范围的缓存。这个缓存中存放的对象的松散数据。第二级对象有可能出现并发问题,因此需要采用适当的并发访问策略,该策略为被缓存的数据提供了事务隔离级别。缓存适配器用于把具体的缓存实现软件与Hibernate集成。第二级缓存是可选的,可以在每个类或每个集合的粒度上配置第二级缓存。
Hibernate的二级缓存策略的一般过程如下:
1) 条件查询的时候,总是发出一条select * from table_name where …. (选择所有字段)这样的SQL语句查询数据库,一次获得所有的数据对象。
2) 把获得的所有数据对象根据ID放入到第二级缓存中。
3) 当Hibernate根据ID访问数据对象的时候,首先从Session一级缓存中查;查不到,如果配置了二级缓存,那么从二级缓存中查;查不到,再查询数据库,把结果按照ID放入到缓存。
4) 删除、更新、增加数据的时候,同时更新缓存。
Hibernate的二级缓存策略,是针对于ID查询的缓存策略,对于条件查询则毫无作用。为此,Hibernate提供了针对条件查询的Query缓存。
Hibernate的Query缓存策略的过程如下:
1) Hibernate首先根据这些信息组成一个Query Key,Query Key包括条件查询的请求一般信息:SQL, SQL需要的参数,记录范围(起始位置rowStart,最大记录个数maxRows),等。
2) Hibernate根据这个Query Key到Query缓存中查找对应的结果列表。如果存在,那么返回这个结果列表;如果不存在,查询数据库,获取结果列表,把整个结果列表根据Query Key放入到Query缓存中。
3) Query Key中的SQL涉及到一些表名,如果这些表的任何数据发生修改、删除、增加等操作,这些相关的Query Key都要从缓存中清空。
缓存同步策略:
缓存同步策略决定了数据对象在缓存中的存取规则,我们必须为每个实体类指定相应的缓存同步策略.Hibernate中提供了4种不同的缓存同步策略:
1.read-only:只读.对于不会发生改变的数据可使用.
2.nonstrict-read-write:如果程序对并发访问下的数据同步要求不严格,且数据更新频率较低,采用本缓存同步策略可获得较好性能.
3.read-write:严格的读写缓存.基于时间戳判定机制,实现了"read committed"事务隔离等级.用于对数据同步要求的情况,但不支持分布式缓存,实际应用中使用最多的缓存同步策略.
4.transactional: 事务型缓存,必须运行在JTA事务环境中.此缓存中,缓存的相关操作被添加到事务中(此缓存类似于一个内存数据库),如事务失败,则缓冲池的数据会一同回 滚到事务的开始之前的状态.事务型缓存实现了"Repeatable read"事务隔离等级,有效保证了数据的合法性,适应于对关键数据的缓存,Hibernate内置缓存中,只有JBossCache支持事务型缓存.
如何配置hibernate cache呢?
在applicationContext.xml文件中添加以下代码: <prop key="hibernate.cache.use_second_level_cache">true</prop> <!--设置缓存机制为二级缓存 -->
<prop key="hibernate.cache.use_query_cache">true</prop> <!--启动查询缓存 -->
<prop key="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</prop> <!--设置二级缓存的Provider类 -->
<prop key="hibernate.cache.provider_configuration_file_resource_path">WEB-INF/classes/ehcache.xml</prop> <!--设置缓存的配置文件路径 -->
将ehcache.xml文件放到src下面,并配置ehcache.xml文件如下:
<ehcache>
<diskStore path="D:cache" />
<!-- maxElementsInMemory为缓存对象的最大数目, eternal设置是否永远不过期,timeToIdleSeconds对象处于空闲状态的最多秒数,timeToLiveSeconds对象处于缓存状态的最多秒数 -->
<defaultCache maxElementsInMemory="10000" eternal="false"
timeToIdleSeconds="3600" timeToLiveSeconds="3600"
overflowToDisk="true" diskPersistent="false"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU" />
<cache name="net.nk.entity.DataAttr"
maxElementsInMemory="800" eternal="false" overflowToDisk="false"
timeToIdleSeconds="3600" timeToLiveSeconds="3600" />
<cache name="net.nk.entity.SmContentCategory"
maxElementsInMemory="800" eternal="false" overflowToDisk="false"
timeToIdleSeconds="3600" timeToLiveSeconds="3600" />
</ehcache>
说明:以上是对DataAttr、SmContentCategory两个实体类配置了二级缓存,你也可以继续添加。
在设置了缓存机制的类的xml文件中添加一段代码
<hibernate-mapping>
<!--SM_PRODUCT表的hibernate映射描述文件 -->
<class name="net.nk.entity.SmProduct" table="SM_PRODUCT" >
<cache usage="read-write" region="net.nk.entity.SmProduct"/>
<!-- ID -->
<id name="id" type="string">
<column name="ID" />
<generator class="assigned" />
</id>
其中类的xml文件中的region设置要和ehcache.xml文件中的保持一致,此处是通过包名+类名的方式。
在调用数据库数据方法时,可采取以下设置:
protected List<POJO> getAll(Class<T> entityClass,boolean iscache) throws SSHException {
HibernateTemplate ht = getHibernateTemplate();
if(iscache){
ht.setCacheQueries(true);
}
return ht.find("from "+entityClass.getName());//getHibernateTemplate().loadAll(entityClass);
}
至此,可以通过放开hibernate的show_sql来查看是否缓存机制生效了。
批量处理:
由于Hibernate对这两种缓存有着不同的管理机制,对于二级缓存,我们可以对它的大小进行相关配置,而对于内部缓存,Hibernate就采取了“放任自流”的态度了,对它的容量并没有限制。现在症结找到了,我们做海量数据插入的时候,生成这么多的对象就会被纳入内部缓存(内部缓存是在内存中做缓存的),这样你的系统内存就会一点一点的被蚕食,如果最后系统被挤“炸”了,也就在情理之中了。
我们想想如何较好的处理这个问题呢?有的开发条件又必须使用Hibernate来处理,当然有的项目比较灵活,可以去寻求其他的方法。
这里推荐两种方法:
(1):优化Hibernate,程序上采用分段插入及时清除缓存的方法。
(2):绕过Hibernate API ,直接通过 JDBC API 来做批量插入,这个方法性能上是最 好的,也是最快的。
对于上述中的方法1,其基本是思路为:优化Hibernate,在配置文件中设置hibernate.jdbc.batch_size参数,来指定每次提交SQL的数量;程序上采用分段插入及时清除缓存的方法(Session实现了异步write-behind,它允许Hibernate显式地写操作的批处理),也就是每插入一定量的数据后及时的把它们从内部缓存中清除掉,释放占用的内存。
设置hibernate.jdbc.batch_size参数,可参考如下配置。
<hibernate-configuration>
<session-factory>
.........
<property name=” hibernate.jdbc.batch_size”>50</property>
.........
<session-factory>
<hibernate-configuration>
配置hibernate.jdbc.batch_size参数的原因就是尽量少读数据库,hibernate.jdbc.batch_size参数值越大,读数据库的次数越少,速度越快。从上面的配置可以看出,Hibernate是等到程序积累到了50个SQL之后再批量提交。
hibernate.jdbc.batch_size参数值也可能不是设置得越大越好,从性能角度上讲还有待商榷。这要考虑实际情况,酌情设置,一般情形设置30、50就可以满足需求了。
Session session=HibernateUtil.currentSession();
Transatcion tx=session.beginTransaction();
for(int i=0;i<10000;i++)
...{
Student st=new Student();
st.setName(“feifei”);
session.save(st);
if(i%50==0) //以每50个数据作为一个处理单元
...{
session.flush(); //保持与数据库数据的同步
session.clear(); //清除内部缓存的全部数据,及时释放出占用的内存
}
}
tx.commit();
.........
在一定的数据规模下,这种做法可以把系统内存资源维持在一个相对稳定的范围。
注意:前面提到二级缓存,在这里有必要再提一下。如果启用了二级缓存,从机制上讲Hibernate为了维护二级缓存,我们在做插入、更新、删除操作时,Hibernate都会往二级缓存充入相应的数据。性能上就会有很大损失,所以建议在批处理情况下禁用二级缓存。
总结:
不要想当然的以为缓存一定能提高性能,仅仅在你能够驾驭它并且条件合适的情况下才是这样的。hibernate的二级缓存限制还是比较多的,不方便用jdbc可能会大大的降低更新性能。在不了解原理的情况下乱用,可能会有1+N的问题。不当的使用还可能导致读出脏数据。
如果受不了hibernate的诸多限制,那么还是自己在应用程序的层面上做缓存吧。
在越高的层面上做缓存,效果就会越好。就好像尽管磁盘有缓存,数据库还是要实现自己的缓存,尽管数据库有缓存,咱们的应用程序还是要做缓存。因为底层的缓存它并不知道高层要用这些数据干什么,只能做的比较通用,而高层可以有针对性的实现缓存,所以在更高的级别上做缓存,效果也要好些吧。
hibernate缓存机制(转载)的更多相关文章
- 10.hibernate缓存机制详细分析(转自xiaoluo501395377)
hibernate缓存机制详细分析 在本篇随笔里将会分析一下hibernate的缓存机制,包括一级缓存(session级别).二级缓存(sessionFactory级别)以及查询缓存,当然还要讨论 ...
- 【转 :Hibernate 缓存机制】
转自:http://www.cnblogs.com/wean/archive/2012/05/16/2502724.html Hibernate 缓存机制 一.why(为什么要用Hibernate缓存 ...
- hibernate缓存机制详解
hiberante面试题—hibernate缓存机制详解 这是面试中经常问到的一个问题,可以按照我的思路回答,准你回答得很完美.首先说下Hibernate缓存的作用(即为什么要用缓存机制),然后再 ...
- hibernate缓存机制与N+1问题
在项目中遇到的趣事 本文基于hibernate缓存机制与N+1问题展开思考, 先介绍何为N+1问题 再hibernate中用list()获得对象: /** * 此时会发出一条sql,将30个学生全部查 ...
- hibernate缓存机制详细分析 复制代码 内部资料 请勿转载 谢谢合作
您可以通过点击 右下角 的按钮 来对文章内容作出评价, 也可以通过左下方的 关注按钮 来关注我的博客的最新动态. 如果文章内容对您有帮助, 不要忘记点击右下角的 推荐按钮 来支持一下哦 如果您对文章内 ...
- Hibernate缓存机制 (2013-07-02 13:51:32)转载▼
标签: java web hibernate 缓存 代码 分类: javaweb 缓存是位于应用程序与物理数据源之间,用于临时存放复制数据的内存区域,目的是为了减少应用程序对物理数据源访 ...
- hibernate缓存机制(转)
原文出处:http://www.cnblogs.com/wean/archive/2012/05/16/2502724.html 一.why(为什么要用Hibernate缓存?) Hibernate是 ...
- Hibernate 缓存机制
一.why(为什么要用Hibernate缓存?) Hibernate是一个持久层框架,经常访问物理数据库. 为了降低应用程序对物理数据源访问的频次,从而提高应用程序的运行性能. 缓存内的数据是对物理数 ...
- hibernate缓存机制详细分析
转自:http://www.cnblogs.com/xiaoluo501395377/p/3377604.html 在本篇随笔里将会分析一下hibernate的缓存机制,包括一级缓存(session级 ...
随机推荐
- 为什么KVM计算机点无故重启?
一.故障1:机器hangs 本地一台cloudstack计算节点无故连不上了,cloudstack也坏了,后查看有一台系统虚拟机在这台计算节点上,导致cs挂了.去找到这台机器后,发现这台机器卡住了,重 ...
- maven将手动下载的jar包安装到本地仓库
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://mave ...
- selenium+python+pycharm环境搭建
1.安装python,配置path环境变量,验证python安装成功与否 2.安装Pycharm:Pycharm是Python的IDE,所以我们需要安装一下:下载安装Selenium 3.在官网(ht ...
- PEM文件和private.key文件生成IIS服务器所需的pfx文件(配置SSL用)
第一步:输入域名,点击"创建免费的SSL证书"按钮,如图 第二步:输入邮箱,点击创建,如图 创建成功后,需要进行dns验证,结果图如下: 第三步:在域名所在的云服务器上,添加域名解 ...
- Proof for Floyd-Warshall's Shortest Path Derivation Algorithm Also Demonstrates the Hierarchical Path Construction Process
(THIS BLOG WAS ORIGINALLY WRTITTEN IN CHINESE WITH LINK: http://www.cnblogs.com/waytofall/p/3732920. ...
- js里面的三种注释方法
javascript(js)语言里面的注释方法有三种. 第一种是多行注释"/**/",一般js文件开头,介绍作者,函数等信息. /* *author:xxx *day:2008-0 ...
- objective-C中的"非正式协议"和“正式协议”
objective-C中的接口与泛型 先承认我是标题党,因为在obj-c的世界中,官方根本没有"接口"与"泛型"这样的说法. 不过在obj-c中有二个与之接近的 ...
- 推荐十几款Firefox web开发插件(转载)
开发工具 Web Developer 1.1.8 https://addons.mozilla.org/en-US/firefox/addon/60by chrispederick The Web ...
- Android开发实战之拥有Material Design风格的折叠布局
关于折叠布局,也许你并不陌生,最新版的陌陌,或者一些其他的社交APP都有一个折叠布局.折叠布局,让我们的APP更加具有交互性,同时也更加美观,先来展示一下效果图: 这是我个人做的一个APP主界面,可以 ...
- 第一个MFC实例:计算圆周长和圆面积
一.基于Microsoft MFC的编程方法 MFC是微软基础类库(Microsoft Foundation Class)的缩写.与API不同,MFC不是Windows操作系统的组成部分,而是微软公司 ...