Spring/Hibernate应用性能调优

对于大多数典型的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查询日志是像这样的:


1

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


1

2

3

@Id

@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "your_key_generator")

private Long id;

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

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

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

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

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


1

2

3

<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会话所导致的性能问题。

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


1

2

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可以像这样来关闭:


1

2

3

4

@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框架的应用中发生。

采集

#HUABAN_WIDGETS .HUABAN-red-normal-icon-button, .HUABAN-red-large-icon-button, .HUABAN-red-small-icon-button, .HUABAN-white-normal-icon-button, .HUABAN-white-large-icon-button, .HUABAN-white-small-icon-button { background-image: url({{imgBase}}/widget_icons_ie6.png)

时间: 2024-12-25 02:31:23

Spring/Hibernate应用性能调优的相关文章

如何进行HIBERNATE性能调优

大体上,对于HIBERNATE性能调优的主要考虑点如下: ? 数据库设计调整 ? HQL优化 ? API的正确使用(如根据不同的业务类型选用不同的集合及查询API) ? 主配置参数(日志,查询缓存,fetch_size, batch_size等) ? 映射文件优化(ID生成策略,二级缓存,延迟加载,关联优化) ? 一级缓存的管理 ? 针对二级缓存,还有许多特有的策略 ? 事务控制策略. 1. 数据库设计 a) 降低关联的复杂性 b) 尽量不使用联合主键 c) ID的生成机制,不同的数据库所提供的

性能调优

性能调优 1.设计调优 宏观层面质的优化 2.代码调优 熟悉相关API,并在合适的场景中正确使用相关API或类库,同时,对算法.数据结构的灵活运用也是代码优化的重要内容 3.JVM调优 代码和JVM属于系统微观层面量的优化 4.数据库调优 使用preparestatement代替statement提高查询效率 Select,使用要查询的具体的列名,避免使用*号, 合理地使用冗余字段 Oracle的分区表 根据不同的数据,以Oracle为例,设置合理大小的共享池.缓存缓冲区或者PGA 5.操作系统

apache性能调优(转)

一.总结前一天的学习 在前两天的学习中我们知道.了解并掌握了Web Server结合App Server实现单向Https的这样的一个架构.这个架构是一个非常基础的J2ee工程上线布署时的一种架构.在前两天的教程中,还讲述了Http服务器.App Server的最基本安全配置(包括单向https的实现), 它只是避免了用户可以通过浏览器侵入我们的Web访问器或者能够通过Web浏览器来查询我们的Web目录结构及其目录内的文件与相关内容,这种入侵我们把它称为: Directory traversal

通向架构师的道路(第三天)之apache性能调优

通向架构师的道路(第三天)之apache性能调优 分类: 架构师之路   2012-07-01 23:30 1434人阅读 评论(2) 收藏 举报 一.总结前一天的学习 在前两天的学习中我们知道.了解并掌握了Web Server结合App Server实现单向Https的这样的一个架构.这个架构是一个非常基础的J2ee工程上线布署时的一种架构.在前两天的教程中,还讲述了Http服务器.App Server的最基本安全配置(包括单向https的实现), 它只是避免了用户可以通过浏览器侵入我们的We

java架构师课程、性能调优、高并发、tomcat负载均衡、大型电商项目实战、高可用、高可扩展、数据库架构设计、Solr集群与应用、分布式实战、主从复制、高可用集群、大数据

15套Java架构师详情 * { font-family: "Microsoft YaHei" !important } h1 { background-color: #006; color: #FF0 } 15套java架构师.集群.高可用.高可扩展.高性能.高并发.性能优化.Spring boot.Redis.ActiveMQ.Nginx.Mycat.Netty.Jvm大型分布式项目实战视频教程 视频课程包含: 高级Java架构师包含:Spring boot.Spring  clo

hbase性能调优(1)

hbase性能调优 标签: hbase 性能调优 | 发表时间:2014-05-17 15:10 | 作者:无尘道长 分享到: 出处:http://www.iteye.com 一.服务端调优 1.参数配置 1).hbase.regionserver.handler.count:该设置决定了处理RPC的线程数量,默认值是10,通常可以调大,比如:150,当请求内容很大(上MB,比如大的put.使用缓存的scans)的时候,如果该值设置过大则会占用过多的内存,导致频繁的GC,或者出现OutOfMem

MySQL性能调优与架构设计——第 14 章 可扩展性设计之数据切分

第 14 章 可扩展性设计之数据切分 前言 通过 MySQL Replication 功能所实现的扩展总是会受到数据库大小的限制,一旦数据库过于庞大,尤其是当写入过于频繁,很难由一台主机支撑的时候,我们还是会面临到扩展瓶颈.这时候,我们就必须许找其他技术手段来解决这个瓶颈,那就是我们这一章所要介绍恶的数据切分技术. 14.1 何谓数据切分 可能很多读者朋友在网上或者杂志上面都已经多次见到关于数据切分的相关文章了,只不过在有些文章中称之为数据的 Sharding.其实不管是称之为数据的 Shard

JVM 性能调优实战之:使用阿里开源工具 TProfiler 在海量业务代码中精确定位性能代码

本文是<JVM 性能调优实战之:一次系统性能瓶颈的寻找过程> 的后续篇,该篇介绍了如何使用 JDK 自身提供的工具进行 JVM 调优将 TPS 由 2.5 提升到 20 (提升了 7 倍),并准确定位系统瓶颈:我们应用里静态对象不是太多.有大量的业务线程在频繁创建一些生命周期很长的临时对象,代码里有问题.那么问题来了,如何在海量业务代码里边准确定位这些性能代码?本文将介绍如何使用阿里开源工具 TProfiler 来定位这些性能代码,成功解决掉了 GC 过于频繁的性能瓶颈,并最终在上次优化的基础

36套精品Java高级课,架构课,java8新特性,P2P金融项目,程序设计,功能设计,数据库设计,第三方支付,web安全,高并发,高性能,高可用,分布式,集群,电商,缓存,性能调优,设计模式,项目实战,大型分布式电商项目实战视频教程

新年伊始,学习要趁早,点滴记录,学习就是进步! QQ:1225462853 视频课程包含: 36套Java精品高级课架构课包含:java8新特性,P2P金融项目,程序设计,功能设计,数据库设计,架构设计,web安全,高并发,高性能,高可用,高可扩展,分布式,集群,电商,缓存,性能调优,设计模式,项目实战,工作流,程序调优,负载均衡,Solr集群与应用,主从复制,中间件,全文检索,Spring boot,Spring cloud,Dubbo,Elasticsearch,Redis,ActiveMQ