目前的项目是一个极少写冲突,多读,多重复HQL语句的项目,因此非常适合使用Hibernate的二级缓存进行查询优化。目前项目使用的均是最新版本的框架,配置成功后很快就成功使用了,大概讲讲配置方法。

1. Hibernate L2缓存

1.1. 缓存的分类

  • 事务缓存:作用于事务范围,session结束则缓存清除,Hibernate的L1缓存为事务缓存,默认开启,我们在纯Hibernate项目中手写过回滚的代码,能够回滚就是因为事务缓存。
  • 应用缓存:作用于应用范围,被所有事务共享,依赖于应用的生命周期。所以,非常适合使用一个同样依赖于应用生命周期的轻量级缓存来实现,ehcache几乎是最好的选择。
  • 集群缓存:该缓存类似于真正的数据库被一个集群共享,典型的如Redis就很适合做集群缓存。

1.2. L2缓存工作原理

Hibernate的L1,L2缓存均是通过id进行工作,当Hibernate根据id访问对象时会先在一级缓存中查找,如果查不到则在二级缓存中查找。

SessionFactory二级缓存根据功能和目的又可以划分为内置缓存和外置缓存,内置缓存存放映射元数据和预定义SQL语句,前者为映射文件中数据的副本,后者为根据副本推导出的SQL语句。内置缓存是只读的,因此不需要与映射文件进行同步。外置缓存是Hibernate的一个插件,默认不启用,即Hibernate的L2缓存。外置缓存的数据是数据库数据的副本,外置缓存的介质可以是内存或者硬盘。

1.3. 放入二级缓存的数据

一般包含以下几种:

  • 很少被修改的数据
  • 不是很重要的数据,允许出现偶尔并发的数据。
  • 不会被并发访问的数据。
  • 常量数据。
  • 不会被第三方修改的数据。

2. Ehcache

Ehcache是一个健壮的简洁的轻量的纯Java进程的内存缓存框架,因此其存在与Java进程直接相关联。通过在硬盘和内存里对数据进行拷贝,实现了数据库的缓存。由于Apache的支持,Ehcache非常稳健。

2.1. 依赖

<!--ehcache依赖slf4j-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.25</version>
</dependency>
<!--slf4j依赖log4j-->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<!--ehcache-->
<dependency>
<groupId>org.ehcache</groupId>
<artifactId>ehcache</artifactId>
<version>3.3.1</version>
</dependency>
<!--hibernate.ehcache-->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-ehcache</artifactId>
<version>${org.hibernate.version}</version>
</dependency>

2.2. ehcache.xml

该文件需要放置src中(Maven项目的resources中),以便编译后在根目录内,也可以显示指定位置。这个文件给出了ehcache的基本配置。

<?xml version="1.0" encoding="UTF-8"?>
<ehcache>
<diskStore path="java.io.tmpdir"/>
<!--没有特殊设置时系统默认使用此设置-->
<defaultCache
maxElementsInMemory="1000"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
overflowToDisk="true"
maxElementsOnDisk="10000"
diskPersistent="false"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU"
/>
<!--想使用查询缓存,这两个类需要添加-->
<cache name="org.hibernate.cache.spi.UpdateTimestampsCache"
maxElementsInMemory="5000"
eternal="true"
overflowToDisk="true" />
<cache name="org.hibernate.cache.internal.StandardQueryCache"
maxElementsInMemory="10000"
eternal="false"
timeToLiveSeconds="120"
overflowToDisk="true" />
<cache name="javaClassName" maxElementsInMemory="2000" eternal="false"
timeToIdleSeconds="120" timeToLiveSeconds="120"
overflowToDisk="true" />
</ehcache>

ehcache的各属性介绍如下:

  • name:缓存名称。
  • maxElementsInMemory:缓存最大个数。
  • eternal:对象是否永久有效,一但设置了,timeout将不起作用。
  • timeToIdleSeconds:设置对象在失效前的允许闲置时间(单位:秒)。仅当eternal=false对象不是永久有效时使用,可选属性,默认值是0,也就是可闲置时间无穷大。
  • timeToLiveSeconds:设置对象在失效前允许存活时间,最大时间介于创建时间和失效时间之间。仅当eternal=false对象不是永久有效时使用,默认是0,也就是对象存活时 间无穷大。
  • overflowToDisk:当内存中对象数量达到maxElementsInMemory时,Ehcache将会对象写到磁盘中。
  • diskSpoolBufferSizeMB:这个参数设置DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区。
  • maxElementsOnDisk:硬盘最大缓存个数。
  • diskPersistent:是否缓存虚拟机重启期数据,默认false。
  • diskExpiryThreadIntervalSeconds:磁盘失效线程运行时间间隔,默认是120秒。
  • memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。默认策略是LRU。你可以设置为 FIFO或是LFU。
  • clearOnFlush:内存数量最大时是否清除。

2.3. 常用的memoryStoreEvictionPolicy(缓存算法)

关于常用的缓存算法主要有三种:

  • LRU:(Least Rencently Used)新来的对象替换掉使用时间算最近很少使用的对象。
  • LFU:(Least Frequently Used)替换掉按命中率高低算比较低的对象。
  • FIFO: (First In First Out)把最早进入二级缓存的对象替换掉。

2.4. ehcache使用

ehcache不支持事务,有三种模式:

  • READ_ONLY: 适用于仅读取,如果有数据的更新操作则会异常。
  • READ_WRITE: 用读写锁控制缓存
  • NON_STRICT_READ_WRITE: 不加锁控制缓存,写写会有冲突,适用于很难发生写冲突的系统。

具体使用时,在hibernate持久化生成的Entity上使用类似这样的标签,即可为该数据库添加二级缓存。

@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)

通常情况下,缓存用于多读少写的表,在这种表中,最高效,最符合缓存本身行为的应该是READ_ONLY模式,即,在读取时使用缓存,发生写操作时清空缓存。

3. Spring配置

3.1. sessionFactory配置

当我们使用Spring的hibernateTemplate时,需要对sessionFactory进行配置,其中有无关于ehcache的部分可以参考Spring4托管Hibernate5并利用HibernateTemplate进行数据库操作,这里主要讲解和ehcache相关的设置。

<bean id="sessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
……
<property name="hibernateProperties">
<props>
……
<prop key="hibernate.cache.use_second_level_cache">true</prop>
<prop key="hibernate.cache.use_query_cache">true</prop>
<prop key="hibernate.cache.region.factory_class">
org.hibernate.cache.ehcache.SingletonEhCacheRegionFactory</prop>
</props>
……
  • hibernate.cache.use_second_level_cache 是hibernate中L2缓存的开关,必须为true。
  • hibernate.cache.use_query_cache 是hibernate的查询缓存的开关,可以自己决定是否开启。
  • hibernate.cache.region.factory_class 承载L2缓存的方法,即选择L2缓存数据库。官方很坑的从hibernate4开始就存在文档问题,文档中仍为provider_class,实际上早已换为了这个方法(idea的默认提示中找不到,但运行后如果没添加,错误日志里可以显示出)。需要注意的是,需要使用Singleton模式的Factory,否则会有冲突问题。具体原因还不明了。

另外有几个可以开启的选项,包括

  • hibernate.generate_statistics 生成统计日志,如果项目在调试,这是一个很好的开发选项。记得实际运行时关闭掉。
  • hibernate.cache.provider_configuration_file_resource_path 提供配置文件的路径,如果你不想使用默认路径,那么需要在这里配置,其格式和web.xml中的路径一致。

3.2. hibernateTemplate配置

其实就是开启一下查询缓存,一条

    <bean id="hibernateTemplate" class="org.springframework.orm.hibernate5.HibernateTemplate">
<property name="sessionFactory" ref="sessionFactory" />
<property name="cacheQueries" value="true"/>
</bean>

4. Hiberante二级缓存的使用

Hibernate的所有查询方法均用到事务缓存,但对于SessionFactory缓存,只有部分方法会使用。

4.1. 不使用二级缓存的方法

Hibernate的各种查询方式中,以下几种方式不使用缓存,直接从数据库读写:

  • get()
  • find()
  • list()

其中后两者在使用hibernateTemplate时均为find()方法。但当开启了查询缓存后,使用这些方法时,同样也会把查询的结果存入缓存,这会造成一定的时间消耗,但是可以有效的避免使用缓存时的N+1问题。

4.2. 使用二级缓存的方法

Hibernate的以下方法使用二级缓存

  • load()
  • iterate()

这里面特别说明一下iterate()方法,该方法返回的是一个指向查询结果的指针,当方法返回指针后,如果想通过指针获取整个查询结果,则需要使用事务,并在表上加如下标签:

@Proxy(lazy = false)

关闭hibernate的懒加载。否则,当想要通过返回的iterator获取其下一方法,iterator.next(),则会因为变量已经进入游离态,无法找到下一方法。即使如此,寻找下一指针的方法也需要和返回iterator的方法处于同一事务内才能成功。

一个对lazy=false产生的损耗的补救方案是使用Spring的OpenSessionInViewFilter来管理session,在web.xml中添加

    <filter>
<filter-name>OpenSessionInViewFilter</filter-name>
<filter-class>org.springframework.orm.hibernate5.support.OpenSessionInViewFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>OpenSessionInViewFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

即可。

4.3. iterate()查询原理和N+1问题

使用iterate()方法时,Hibernate会先访问数据库,查询所有要查询对象的id,再访问缓存,通过id查询所有要查询对象,当对象在缓存中时,直接返回结果,当对象不再缓存中时,访问数据库查询该对象。因此,当缓存没有建立时,这样的查询方法会产生N+1次查询,远比find()方法的1次数据库查询效率低下。所以,简单的使用iterator对数据进行查询是十分不合理的,两种方案可以考虑。

  • 在用户访问前,对数据库中常用数据进行缓存,比如,在程序启动后自动执行一次find()行为把常用数据进行存储。
  • 用户的第一次访问使用find()方法,并获取缓存,之后的访问使用iterate()方法。

5. 参考文档

CacheConcurrencyStrategy的五种缓存方式的简单介绍

ehcache memcache redis 三大缓存男高音

Hibernate4之二级缓存配置

Hibernate二级缓存简述及基于Spring4,Hibernate5,Ehcache3的二级缓存配置的更多相关文章

  1. [原创]java WEB学习笔记93:Hibernate学习之路---Hibernate 缓存介绍,缓存级别,使用二级缓存的情况,二级缓存的架构集合缓存,二级缓存的并发策略,实现步骤,集合缓存,查询缓存,时间戳缓存

    本博客的目的:①总结自己的学习过程,相当于学习笔记 ②将自己的经验分享给大家,相互学习,互相交流,不可商用 内容难免出现问题,欢迎指正,交流,探讨,可以留言,也可以通过以下方式联系. 本人互联网技术爱 ...

  2. 基于Spring4+SpringMVC4+Mybatis3+Hibernate4+Junit4框架构建高性能企业级的部标1077视频监控平台

    开发企业级的部标GPS监控平台,投入的开发力量很大,开发周期也很长,选择主流的开发语言以及成熟的开源技术框架来构建基础平台,是最恰当不过的事情,在设计之初就避免掉了技术选型的风险,避免以后在开发过程中 ...

  3. 【Java EE 学习 47】【Hibernate学习第四天】【sesion】【一级缓存】【懒加载】

    一.Session概述 1.Session 接口是 Hibernate 向应用程序提供的操纵对数据库的最主要的接口, 它提供了基本的保存, 更新, 删除和加载Java 对象的方法. 2.理解Sessi ...

  4. 基于Spring4+SpringMVC4+Mybatis3+Hibernate4+Junit4框架构建高性能企业级的部标GPS监控平台

    开发企业级的部标GPS监控平台,投入的开发力量很大,开发周期也很长,选择主流的开发语言以及成熟的开源技术框架来构建基础平台,是最恰当不过的事情,在设计之初就避免掉了技术选型的风险,避免以后在开发过程中 ...

  5. hibernate框架学习第三天:对象状态、一级缓存、快照等

    对象的状态 瞬时状态: 瞬时对象(TO) 应用程序创建出来的对象,不受H3控制 注意:TO对象不具有OID,一旦为TO赋值OID,那么此时就不是TO 持久化状态:持久化对象(PO) 受H3控制的对象, ...

  6. 使用Spring提供的缓存抽象机制整合EHCache为项目提供二级缓存

      Spring自身并没有实现缓存解决方案,但是对缓存管理功能提供了声明式的支持,能够与多种流行的缓存实现进行集成. Spring Cache是作用在方法上的(不能理解为只注解在方法上),其核心思想是 ...

  7. Spring4.1新特性——Spring缓存框架增强(转)

    目录 Spring4.1新特性——综述 Spring4.1新特性——Spring核心部分及其他 Spring4.1新特性——Spring缓存框架增强 Spring4.1新特性——异步调用和事件机制的异 ...

  8. 基于spring4.0配置分布式ehcache,以及相关使用

    说明:本文是基于RMI手动同步的方式,使用程序动态注入配置缓存,抛弃传统的ehcache.xml配置方式 1,注入cacheManager管理所有缓存,添加各个缓存名及相关参数配置: 思路大致是: 在 ...

  9. 基于Spring4的定时任务管理

    在项目中,有时会遇到定时任务的处理,下面介绍一下我的做法. 此做法基于Spring4,Spring框架搭建成功,另需引入quartz.jar,pom.xml文件中加入 <dependency&g ...

随机推荐

  1. Eclipse 创建 Maven 项目、Maven JavaWeb 项目

    Eclipse 创建 Maven 项目         新建一个maven项目          (默认)(如果不行第一个Create a simple ... 也选中) 默认         Jav ...

  2. mysql进阶(二十)CPU超负荷异常情况

    CPU超负荷异常情况 问题 项目部署阶段,提交订单时总是出现cpu超负荷工作情况,导致机器卡死,订单提交失败.通过任务管理器可见下图所示: 通过任务管理器中进程信息(见下图)进行查看,可见正是由于项目 ...

  3. 菜鸟玩云计算之廿二: saltstack 配置

    菜鸟玩云计算之廿二: saltstack 配置 要求环境: RHEL6.4+ >=Python2.6.6, < Python 3.0 关闭salt-master/minion服务:   # ...

  4. Material Design之CardView的使用

    本文介绍CardView这个控件的使用,CardView继承至FrameLayout类,是support-v7包下的一个类,使用时必须引入cardview依赖包,可在下载的sdk文件夹中找到... 使 ...

  5. infiniDB在linux下完成倒库

    在网看到自己的文章被四处烂用,经常搜到自己的文章.关键是,你能把我头像删除了不,有本事,你 把网址也给出http://blog.csdn.net/longshenlmj/article/details ...

  6. 苹果新的编程语言 Swift 语言进阶(一)--综述

    Swift 是苹果开发和提供的供开发IOS 和OS X应用的一门新的语言.Swift语言基于C 和Objective-C语言,除了提供C 和Objective-C语言具有的所有语法功能外,为了编程方便 ...

  7. 【Qt编程】3D迷宫游戏

    说起迷宫想必大家都很熟悉,个人感觉迷宫对人的方向感是很大的考验,至少我的方向感是不好的,尤其是在三维空间中.由于这段时间帮导师做项目用到了三维作图,便心血来潮想做个三维迷宫玩玩.要想画出三维的迷宫游戏 ...

  8. mahout系列----Dirichlet 分布

    Dirichlet分布可以看做是分布之上的分布.如何理解这句话,我们可以先举个例子:假设我们有一个骰子,其有六面,分别为{1,2,3,4,5,6}.现在我们做了10000次投掷的实验,得到的实验结果是 ...

  9. 《万能数据库查询分析器》实现使用SQL语句直接高效地访问文本文件

    <万能数据库查询分析器>实现使用SQL语句直接高效地访问文本文件 马根峰 (广东联合电子服务股份有限公司, 广州 510300) 摘要    用SQL语句来直接访问文本文件?是在做梦吗? ...

  10. bash配置文件说明

    login shell: /etc/profile 所有用户全局设定,它首先会调用以下文件:     /etc/inputrc     /etc/profile.d/*.sh     /etc/sys ...