Spring/Hibernate应用性能调优

标签: 基础技术 Hibernate Spring 性能优化 | 发表时间:2014-07-09 08:00 | 作者:Grey
出处:http://www.importnew.com

对于大多数典型的Spring/Hibernate 企业应用来说,应用程序的性能几乎完全取决于它的持久层的性能。

这篇文章将会对如何确认在“数据库约束”的应用前,使用7种“快速见效”的技巧来帮助我们提升应用性能。

如何确认一个应用受到“数据库约束”

为了验证一个应用程序是否受到“数据库约束”,首先在一些开发环境中做一些普遍的行为,即使用 VisualVM来监控。 VisualVM是一个搭载JDK的Java解析器,它通过调用 jvisualvm来进行命令行登陆。

登陆Visual VM后按照这样做:

  • 运行你的应用程序
  • 选择 Sampler
  • 点击 Settings复选框
  • 选择 Profile only packages,同时引入下面的包:
    • your.application.packages.*
    • org.hibernate.*
    • org.springframework.*
    • your.database.driver.package, for example oracle.*
    • Click Sample CPU

一个典型“数据库约束”应用的CPU性能分析应该像这样:

 

我们可以看到 Java进程的客户端花费了 56%的时间用来等待数据库通过网络返回结果。

这是个好的标志,它显示了是什么让数据库查询应用变慢的。 32.7%的Hibernate反射调用可以正常运行所以没什么可以改进的。

第一步优化:获得运行基线

做优化的第一步是定义一个基线运行的程序。我们需要确定一组有效的输入数据使程序通过一个类似于生产输出的典型执行类。

主要的区别在于,基线运行应该在更短的时间内运行,作为一个指导方针的执行时间,5到10分钟是一个理想的时间。

如何得到一个好的基线?

一个好的基线应该有以下特点:

  • 功能正确
  • 在类型方面输入数据类似于输出数据
  • 能在很短的时间内完成
  • 基线运行的优化可以进行推广

良好的基线能事半功倍。

怎样会得到一个糟糕的基线?

例如,在批处理调用一个通信系统的数据记录中,获取前 10 000 条记录是 错误的方法。

原因是这前10 000个数据可能是语音通话,而这些未知的错误可能是由于以短信的方式来处理导致的。大量采用这些记录会导致产生一个错误的基线,由此错误的结论就产生了。

手机SQL日志和查询计时

SQL查询的执行时间可以收集用于 log4jdbc。看这篇博客是如何使用log4jdbc收集SQL查询 – Spring/Hibernate improved SQL logging with log4jdbc

查询执行时间衡量的是Java客户端,它包括往返到数据库的网络。SQL查询日志是像这样的:

16 avr. 2014 11:13:48 | SQL_QUERY /* insert your.package.YourEntity */ insert into YOUR_TABLE (...) values (...) {executed in 13 msec}

好的语句本身也是一个不错的信息来源 –他们允许轻松地识别频繁的查询 类型。他们记录在以下这篇博客日志 – 为什么 Hibernate 做这样的 SQL 查询?

SQL日志可以找出哪些指标

SQL 日志可以回答下列问题:

  • 执行最慢的查询是什么?
  • 最常见的查询是什么?
  • 生成主键的时间量是多少?
  • 有数据可以受益于缓存吗?

如何解析SQL日志

可能对于大量日志唯一可行的选择是使用命令行工具。这种方法的优点是非常灵活的。

写一个脚本或命令后我们可以提取主要指标。只要你觉得合适任何命令行工具都是可以用的。

如果你是使用Unix命令行,bash可能是一个不错的选择。Bash也可以在Windows工作站上使用,例如使用 Cygwin,或者包括bash命令行的 Git

频繁应用 Quick-Wins

Quick-wins 能识别Spring/Hibernate应用中的常见问题并找到相应的解决方案。

Quick-win 技巧 1:减少主键生成开销

在‘insert-intensive’的进程中,主键生成策略的选择非常重要。一种常见的生成id的方法是’s用数据库序列,通常每个表进行插入数据的时候避免争用一个同一资源。

问题是如果插入50条记录,我们想避免为了获得50条记录的id而造成的网络往返对数据库的消耗,这会导致大部分时间保持Java进程挂起。

Hibernate通常是如何处理的呢?

Hibernate提供了新的优化后的ID生成器来避免这个问题。这就是序列,一个 HiLo id 生成器是在默认情况下使用的。这是HiLo序列发生器如何工作的:

  • 一旦调用一个序列和1000个(高值的)
  • 像这样来计算 50 个id的序列:
    • 1000 * 50 + 0 = 50000
    • 1000 * 50 + 1 = 50001
    • 1000 * 50 + 49 = 50049, 最低值 (50)
    • 调用更高的序列值 1001 … 等等 …

从一个序列的调用中可以看出,生产50个键可以减少很多网络传输所造成的开销。

这些新的主键优化的产生默认是基于Hibernate 4的,同时也可以在必要时将 hibernate.id.new_generator_mappings设置为false来关掉它。

为什么生成主键依旧是个问题?

问题就是,如果你宣布主键生成策略是 AUTO,优化后 仍然是关闭的状态, 这样的话您的应用程序最终会调用大量的序列。

为了确保新的优化生成器处于运行状态,保证使用 SEQUENCE策略而不是 AUTO

@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "your_key_generator")
private Long id;

通过这个简单的改变,有 10%-20%的范围可以改善,这可以在基本上没有修改代码的情况下在‘insert-intensive’应用程序中采用。

Quick-win 技巧 2:使用JDBC批处理插入/更新

对于批处理程序,JDBC驱动程序通常提供优化来减少网络流量,这就是‘JDBC 批量插入/更新’。当使用这些的时候,驱动级别的插入/更新在发送到数据库前被排入队列。

当达到阈值后整个批处理队列语句一次性发送到数据库。这可以防止驱动逐条发送语句,这可以进行多网络传输。

这是工厂配置的实体管理来激活插入/更新的批处理:

<prop key="hibernate.jdbc.batch_size">100</prop>
<prop key="hibernate.order_inserts">true</prop>
<prop key="hibernate.order_updates">true</prop>

只配置JDBC批处理的大小是 不能正常工作的。这是因为JDBC驱动程序只有当接收到插入/更新完全相同的表时才会进行批处理插入操作。

如果新表收到一条插入语句,那么JDBC驱动会在开始对新表进行批处理操作前首先刷新前一张表的批处理语句。

如果使用Spring批处理的话,一个类似的功能是隐式地进行使用。这种优化可以很容易地完成 30%40%的‘insert intensive’程序,而无需改动一行代码。

Quick-win 技巧 3:定期刷新和清理Hibernate会话

当添加/修改数据库的数据时,Hibernate保持了一个已经存在的实体版本的会话,以防在会话关闭之前进行修改。

但是很多时候,一定在数据库中有匹配的插入时我们就可以安全地丢弃实体。这在Java客户机进程中释放了内存,防止长时间运行Hibernate会话所导致的性能问题。

像这样长时间运行的会话 应该尽可能被阻止,但是由于某种原因需要它们的话就应该包含内存是如何消耗的:

entityManager.flush();
entityManager.clear();

flush将触发插入新实体从而发送到数据库。 clear 则从会话释放新的实体。

Quick-win 技巧 4 :减少Hibernate过多的dirty-checking

Hibernate使用内部的一种机制来保持记录修改的实体的方式就叫做 dirty-checking。这种机制 不是基于实体的equals和hashcode方法的类。

Hibernate能让dirty-checking的性能成本降至最低,dirty-check只会在需要的时候出现,但是这种机制也是有代价的,它有更多的表和列。

在应用做任何优化前,最重要的是测量使用VisualVM所耗费的dirty-checking的成本。

如何避免dirty-checking?

我们所知道的Spring事务方法是只读的,dirty-checking可以像这样来关闭:

@Transactional(readOnly=true)
public void someBusinessMethod() {
    ....
}

另一种避免dirty-checking的方式是使用Hibernate无状态会话,这在 documentation有详细描述。

Quick-win 技巧 5:搜寻 “差的” 查询计划

在最慢的查询列表里进行检查来看他们是否有良好的查询计划。最常见的“差劲的”查询计划是:

  • 全表扫描:表完全地被扫描是因为经常缺少索引或者过时的表统计。
  • 笛卡尔连接:这意味着几张表进行笛卡儿积后的结果正在进行计算。检查正在丢失的连接条件,或者可以通过将一个步骤分为几步来完成可以避免发生这个问题。

Quick-win 技巧 6:检查错误的提交时间间隔

如果你是正在做批处理,那么提交间隔会在性能结果上产生很大的影响, 能达到10-100倍甚至更多。

确认提交间隔是所预期的(通常是Spring批处理作业的100-1000倍)。参数配置错误的情况时有发生。

Quick-win 技巧 7:使用二级查询缓存

如果某些数据被确定为合格缓存,那么看看这篇博客如何设置Hibernate缓存的: Pitfalls of the Hibernate Second-Level / Query Caches

总结

为了解决应用程序性能问题,最重要的操作是收集一些指标来找到当前的瓶颈是什么。

不给定指标的话几乎不可能在有意义的时间内发现是什么导致问题发生的。

同时,许多但并非所有 ‘数据库-驱动’ 的表现缺陷可以避免在最先使用的Spring Batch框架的应用中发生。

相关文章

相关 [spring hibernate 应用] 推荐:

spring+hibernate多数据源的应用

- - CSDN博客推荐文章
我有两个数据库test,和test1,两个库里都有一张表TEST_ONE. applicationContext.xml的配置如下. //数据库test1配置.   //整合两个数据源,指定数据源管理类.    //数据库test.    //数据库test1. //这个类是用来管理数据源的,配置文件中.

Spring/Hibernate应用性能调优

- - ImportNew
对于大多数典型的Spring/Hibernate 企业应用来说,应用程序的性能几乎完全取决于它的持久层的性能. 这篇文章将会对如何确认在“数据库约束”的应用前,使用7种“快速见效”的技巧来帮助我们提升应用性能. 如何确认一个应用受到“数据库约束”. 为了验证一个应用程序是否受到“数据库约束”,首先在一些开发环境中做一些普遍的行为,即使用 VisualVM来监控.

Spring / Hibernate应用性能调优

- - ImportNew
对大部分典型的Spring/Hibernate企业应用来说,应用的性能大部分由持久层的性能决定. 这篇文章会重温一下怎么去确认我们的应用是否是”数据库依赖(data-bound)”( 译者注:即非常依赖数据库,大量时间花在数据库操作上),然后会大概过一下7个常用的提升应用性能的速效方案. 怎么确定应用是否是“数据库依赖”.

Spring中hibernate配置

- - Oracle - 数据库 - ITeye博客
Hibernate方言(Dialect)的类名 - 可以让Hibernate使用某些特定的数据库平台的特性. 在生成的SQL中,scheml/tablespace的全限定名取值. 自动把创建的SessionFactory以这个名字绑定到JNDI中去.取值. 请使用max_fetch_depth. 对单根联合(一对一,多对一),设置外连接抓取树的最大深度.

Spring/Hibernate 应用性能优化的7种方法

- - IT瘾-geek
【编者按】对于大多数典型的 Spring/Hibernate 企业应用而言,其性能表现几乎完全依赖于持久层的性能. 此篇文章中将介绍如何确认应用是否受数据库约束,同时介绍七种常用的提高应用性能的速成法:. 如何确认应用是否受限于数据库. 确认应用是否受限于数据库的第一步,是在开发环境中进行测试,并使用 VisualVM 进行监控.

在Spring、Hibernate中使用Ehcache缓存

- - BlogJava-首页技术区
前一篇 http://www.blogjava.net/hoojo/archive/2012/07/12/382852.html介绍了Ehcache整合Spring缓存,使用页面、对象缓存;这里将介绍在Hibernate中使用查询缓存、一级缓存、二级缓存,整合Spring在HibernateTemplate中使用查询缓存.

spring+hibernate+atomikos 分布式事务管理

- - 企业架构 - ITeye博客
网上有很多的atomikos的分布式事务管理的配置,但是大多数都是同一类型的数据库,并没有跨数据库类型的配置. 使用的数据库是Oracle和mysql. . .

Struts2、Spring、Hibernate 高效开发的最佳实践

- senyo - IBM developerWorks 中国 : 文档库
Struts2、Spring、Hibernate(SSH)是最常用的 Java EE Web 组件层的开发技术搭配,网络中和许多 IT 技术书籍中都有它们的开发教程,但是通常的教程都会让很多程序员陷入痛苦的配置与修改配置的过程. 本文利用 SSH 中的技术特性,利用 Java 反射技术,按照规约优于配置的原理,基于 SSH 设定编写了一个通用开发框架,这使得开发者可以专注于业务逻辑的开发,而不用随着业务增加而添加或修改任何配置,并且对于权限控制和日志记录也提供了方便的接口.

spring+hibernate+JTA 分布式事务的例子 .

- - 编程语言 - ITeye博客
对于横跨多个Hibernate SessionFacotry的分布式事务,只需简单地将 JtaTransactionManager 同多个 LocalSessionFactoryBean 的定义结合起来作为事务策略. 你的每一个DAO通过bean属性得到各自的 SessionFactory 引用. 如果所有的底层JDBC数据源都是支持事务的容器,那么只要业务对象使用了 JtaTransactionManager 作为事务策略,它就可以横跨多个DAO和多个session factories来划分事务,而不需要做任何特殊处理.

Best Performance Practices for Hibernate 5 and Spring Boot 2 (Part 1) - DZone Java

- -
Description:If not, then is important to know that attributes can be loaded lazily, as well via Hibernate bytecode instrumentation (another approach is via subentities).