您的位置:首页 > 其它

心得15-hibernate的优化2-缓存(一级、二级)

2013-01-05 07:44 169 查看
1.缓存概述

缓存(cache)在java应用程序中是一组内存中的集合实例。它保存着永久性存储源(如硬盘上的文件或者数据库)中数据的备份,它的读写速度比读写硬盘的速度快。应用程序在运行时直接读写缓存中的数据,只在某些特定时刻安排缓存中的数据来同步更新数据存储源。如果缓存中存放的数据量非常大,也会用硬盘作为缓存的物理介质。

缓存的作用就是降低应用程序直接读写永久性数据存储源的频率,从而增强应用的运行性能。

缓存的实现不仅需要作为物理介质的硬件(内存),同时还需要用于管理缓存并发访问和过期等策略的软件。

2
缓存范围分类


缓存的范围决定了缓存的生命周期及其可以被谁访问。缓存的范围分为以下三类:

1)事务范围:缓存只能被当前事务访问。缓存的生命周期依赖于事务的生命周期,当事务结束时,缓存也就结束生命周期。在此范围下,缓存的介质是内存。注:一级缓存即session缓存其实就是事务范围内的缓存。

2)进程范围:缓存被进程内的所有事务共享。这些事务有可能是并发访问缓存,因此必须对缓存采取必要的事务隔离机制。注:二级缓存就是进程范围内的缓存。

3)集群范围:在集群环境中,缓存被一个机器或者多个机器的进程共享。缓存的数据被复制到集群环境中的每个进程节点,进程间通过远程通信来保证缓存中的数据一致性。对于大多数应用来说,应该慎用集群范围的缓存,因为访问的速度并不一定比直接访问数据库数据的速度快很多。

3.
缓存的并发访问策略


在进程范围或集群范围的缓存,会出现并发问题。因此可以设置4中类型的并发访问策略,每一种策略对应一种事务隔离级别。事务的隔离级别越高,并发性能就越低。

1)事务型(Transactional)策略

2)读写型(Read-Write)策略

3)非严格读写型(Nonstrict-read-write)策略

4)只读型(Read-only)策略

4.Hibernate中的缓存

Hibernate中提供两级缓存,第一级别的缓存是Session级别的缓存,它是属于事务范围的缓存。

第二级别的缓存是SessionFactory级别的缓存,它是属于进程范围的缓存,这一级别的缓存可以进行配置和更改,并且可以动态加载和卸载。

Hibernate还为查询结果提供一个查询缓存,它依赖于二级缓存。

5.一级缓存的管理

Session级别的缓存由hibernate自动管理。当应用程序调用Session的CRUD方法及调用查询接口的list(),iterate()等方法时,如果在Session缓存中还不存在相应的对象,Hibernate就会把改对象加入到Session缓存中。如果在Session缓存中已经存在这个对象,就不需要再去数据库加载而是直接使用缓存中的这个对象,可以减少访问数据库的频率,提高程序的运行效率。当Hibernate清理缓存时(默认是提交事务的时候),Hibernate会根据缓存中对象的状态来同步数据库中的数据状态,在关闭Session时,会清空Session缓存中的所有对象。

一级缓存不能控制缓存的数量,所以要注意大批量操作数据时可能造成内存溢出;可以用

evict(Object obj):从缓存中清除指定的持久化对象。

clear():清空缓存中所有持久化对象。

flush():进行清理缓存(此时缓存中的数据并不丢失)的操作,让缓存和数据库同步 执行一系列sql语句,但不提交事务

commit():先调用flush() 方法,然后提交事务. 则意味着提交事务,意味着对数据库操作永久保存下来。

可以写一个for循环,Session可以批量插入上万条数据。如下面的代码:

for(int i=0;i<10000;i++){

Session.save(object);

}

这样写的代码会产生一个什么问题?会使一万个对象的缓存全部存在于内存中,这样做加大了内存的压力。所以应该定期清理session的缓存。

当做批量插入或批量更新时,必须通过经常调用Session的flush()以及稍后调用clear()来控制一级缓存的大小,这样内存才能保证足够的空间。

6.二级缓存的管理

二级缓存是SessionFactory级别的缓存,它的使用过程如下:

1)执行条件查询的时候,发出“select * from table_name where …”这样的SQL语句查询数据库,一次获得所有的数据对象。

2)把获得的所有数据对象根据ID放入到二级缓存中。

3)当Hibernate根据ID访问数据对象时,首先从Session缓存中查;查不到,如果配置了二级缓存,那么从二级缓存中查;还没查到,再查询数据库,把结果按照ID放入到二级缓存。

4)删除、更新和增加数据的时候,同时会更新到二级缓存中。

Hibernate的二级缓存策略是针对ID查询的缓存策略,对于调节查询则毫无作用。为此,hibernate提高了单独针对条件查询的查询缓存。

7
常用的缓存插件

Hibernate的二级缓存是一个插件。下面是几种常用的缓存插件。









8.二级缓存配置

配置二级缓存时,考虑的问题是决定哪些持久化类需要使用二级缓存,缓存的并发访问策略选择哪一种,然后再根据这些情况来选择合适的缓存插件,编辑该插件的配置文件。

9.查询缓存

如果在实际使用中对于某个条件查询语句经常使用相同的条件值进行查询,就可以启用查询缓存。

Hibernate的查询缓存策略的过程如下。

1)在第一次执行条件查询时,Hibernate首先根据这些条件值组成一个Query key,Query key包括条件查询的请求信息。

2)在后续的过程中用相同的条件值执行这个查询是,Hibernate就先根据这个Query key到查询缓存中查找对应的结果列表,如果存在,返回。如果不存在,查数据库,再把结果列表根据Query 可以存放在查询缓存中。

所以,只有当经常使用相同的参数值进行相同的条件查询时,才能从查询缓存策略中得到好处。

使用查询缓存的步骤:

1).配置查询缓存

Hibernate提供了3种与查询相关的缓存区域。

默认的查询缓存区域:org.hibernate.cache.StandardQueryCache

用户自定义的查询缓存区域

时间戳缓存区域:org.hibernate.cache.UpdateTimestampCache

在ehcache的配置文件ehcache.xml中设置查询缓存区域的属性。

<ehcache>

<!-- 设置默认的查询缓存区域的属性 -->

<cache name="org.hibernate.cache.StandardQueryCache"

maxElementsInMemory="50"

eternal="false"

overflowToDisk="true"

timeToIdleSeconds="3600"

timeToLiveSeconds="7200"

/>

<!-- 设置时间戳缓存区域的属性 -->

<cache name="org.hibernate.cache.UpdateTimestampsCache"

maxElementsInMemory="500"

eternal="true"

overflowToDisk="true"

/>

<!-- 设置自定义命名查询缓存区域的属性 -->

<cache name="myCacheRegion"

maxElementsInMemory="1000"

eternal="false"

overflowToDisk="true"

timeToIdleSeconds="3600"

timeToLiveSeconds="7200"

/>

</ehcache>

2).打开查询缓存

在hibernate的全局配置文件中添加如下配置来启动查询缓存。

<!--启用查询缓存 -->

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

3).在应用程序代码中使用查询缓存

虽然按以上步骤设置好查询缓存,但hibernate在执行条件查询时默认是忽略查询缓存的。如果希望启用查询缓存,应该调用Query接口的setCacheeable(true)方法。(下面有具体案例分析)

10.
一级缓存案例分析

Hibernate.cfg.xml中要配置如下数据:

<!--
开启二级缓存 -->

<property
name="cache.use_second_level_cache">true</property>

<!-- 提供二级缓存插件(缓存类) -->

<property
name="cache.provider_class">org.hibernate.cache.EhCacheProvider</property>

<!-- 开启查询缓存 -->

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

//请勿模仿测试,会是数据库崩溃导致死机,数量太庞大也会使控制台不停刷屏,致使myeclipse死机

@Test

publicvoid testCache() {

Session session = HibernateUtil.getSession();

session.beginTransaction();

for (int i = 0; i < 50; i++) {

Customer c = new Customer();

c.setCname("凯" + i);

session.save(c);

for (int j = 0; j < 100; j++) {

Orders o = new Orders();

o.setOname("键盘" + j);

Set<Orders> ord = new HashSet<Orders>();

ord.add(o);

c.setOrd(ord);

session.save(o);

if (j % 50 == 0) {

session.flush();

session.clear();

}

}

}

session.getTransaction().commit();

HibernateUtil.close();

}

//测试cache缓存

@Test

publicvoid testCache(){

Session session = HibernateUtil.getSession();

session.beginTransaction();

User user = (User) session.get(User.class,1);
//第一次执行CURD时创建session,从数据库中获取数据,因此有数据,有sql语句

System.out.println(user.getName());

System.out.println("------------------");

//清除user对象,这时后面的这个get()方法会重新建立一个session缓存,也会有sql语句输出

session.evict(user);

User user1 = (User) session.get(User.class,1);
//第二次执行CURD时不创建session,不从数据库中获取数据,因此没有sql语句,有数据是从session缓存中获取到的

System.out.println(user1.getName());

session.getTransaction().commit();

HibernateUtil.close();

//后面这个实验,如果util类的session关闭的方法里用的session没有在线程池中,则无法实现,会报错:session
is closed!

System.out.println("================");

session = HibernateUtil.getSession();

session.beginTransaction();

user = (User) session.get(User.class,1);
//第一次执行CURD时创建session,从数据库中获取数据,因此有数据,有sql语句

System.out.println(user.getName());//上面已经关闭了session,所以这里重新重建session,会执行sql语句,同第一种的get()方法一样

session.getTransaction().commit();

HibernateUtil.close();

}

//测试更新操作,二级缓存也起作用,关闭session再开启执行查询的时候就会从二级缓存获取,不发sql语句

//测试查询缓存

publicvoid model(){

Session session = HibernateUtil.getSession();

session.beginTransaction();

//下面的hql语句,select是可以省略的就是直接从from写起,但是如果你非要写上那么那写select
u(对象别名);不能写select *;后者是sql语句的写法,与hql语句不同

Query query = session.createQuery("select u from User u where u.name='ggg'");

/*

* 设置查询缓存

* * 如果查询缓存存在直接返回

* * 如果查询缓存不存在查询数据库将查询结果放到查询缓存中

*/

query.setCacheable(true);

User user = (User)query.uniqueResult();

System.out.println(user.getName()+"----"+user.getBirthday());

session.getTransaction().commit();

HibernateUtil.close();

}

@Test

publicvoid testCache1() {

model();

System.out.println("----------------");

model();

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: