标签:des style blog http color java os 使用 io
对于大多数典型的Spring/Hibernate 企业应用来说,应用程序的性能几乎完全取决于它的持久层的性能。
这篇文章将会对如何确认在“数据库约束”的应用前,使用7种“快速见效”的技巧来帮助我们提升应用性能。
为了验证一个应用程序是否受到“数据库约束”,首先在一些开发环境中做一些普遍的行为,即使用VisualVM来监控。 VisualVM是一个搭载JDK的Java解析器,它通过调用jvisualvm
来进行命令行登陆。
登陆Visual VM后按照这样做:
Settings
复选框Profile only packages
,同时引入下面的包:
your.application.packages.*
org.hibernate.*
org.springframework.*
your.database.driver.package
, for example oracle.*
Sample CPU
一个典型“数据库约束”应用的CPU性能分析应该像这样:
我们可以看到 Java进程的客户端花费了56%
的时间用来等待数据库通过网络返回结果。
这是个好的标志,它显示了是什么让数据库查询应用变慢的。32.7%
的Hibernate反射调用可以正常运行所以没什么可以改进的。
做优化的第一步是定义一个基线运行的程序。我们需要确定一组有效的输入数据使程序通过一个类似于生产输出的典型执行类。
主要的区别在于,基线运行应该在更短的时间内运行,作为一个指导方针的执行时间,5到10分钟是一个理想的时间。
一个好的基线应该有以下特点:
良好的基线能事半功倍。
例如,在批处理调用一个通信系统的数据记录中,获取前 10 000 条记录是错误的方法。
原因是这前10 000个数据可能是语音通话,而这些未知的错误可能是由于以短信的方式来处理导致的。大量采用这些记录会导致产生一个错误的基线,由此错误的结论就产生了。
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 日志可以回答下列问题:
可能对于大量日志唯一可行的选择是使用命令行工具。这种方法的优点是非常灵活的。
写一个脚本或命令后我们可以提取主要指标。只要你觉得合适任何命令行工具都是可以用的。
如果你是使用Unix命令行,bash可能是一个不错的选择。Bash也可以在Windows工作站上使用,例如使用 Cygwin,或者包括bash命令行的 Git 。
Quick-wins 能识别Spring/Hibernate应用中的常见问题并找到相应的解决方案。
在‘insert-intensive’的进程中,主键生成策略的选择非常重要。一种常见的生成id的方法是’s用数据库序列,通常每个表进行插入数据的时候避免争用一个同一资源。
问题是如果插入50条记录,我们想避免为了获得50条记录的id而造成的网络往返对数据库的消耗,这会导致大部分时间保持Java进程挂起。
Hibernate提供了新的优化后的ID生成器来避免这个问题。这就是序列,一个 HiLo
id 生成器是在默认情况下使用的。这是HiLo序列发生器如何工作的:
从一个序列的调用中可以看出,生产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’应用程序中采用。
对于批处理程序,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’程序,而无需改动一行代码。
当添加/修改数据库的数据时,Hibernate保持了一个已经存在的实体版本的会话,以防在会话关闭之前进行修改。
但是很多时候,一定在数据库中有匹配的插入时我们就可以安全地丢弃实体。这在Java客户机进程中释放了内存,防止长时间运行Hibernate会话所导致的性能问题。
像这样长时间运行的会话应该尽可能被阻止,但是由于某种原因需要它们的话就应该包含内存是如何消耗的:
1
2
|
entityManager.flush(); entityManager.clear(); |
flush
将触发插入新实体从而发送到数据库。clear
则从会话释放新的实体。
Hibernate使用内部的一种机制来保持记录修改的实体的方式就叫做 dirty-checking。这种机制不是基于实体的equals和hashcode方法的类。
Hibernate能让dirty-checking的性能成本降至最低,dirty-check只会在需要的时候出现,但是这种机制也是有代价的,它有更多的表和列。
在应用做任何优化前,最重要的是测量使用VisualVM所耗费的dirty-checking的成本。
我们所知道的Spring事务方法是只读的,dirty-checking可以像这样来关闭:
1
2
3
4
|
@Transactional (readOnly= true ) public void someBusinessMethod() { .... } |
另一种避免dirty-checking的方式是使用Hibernate无状态会话,这在documentation有详细描述。
在最慢的查询列表里进行检查来看他们是否有良好的查询计划。最常见的“差劲的”查询计划是:
如果你是正在做批处理,那么提交间隔会在性能结果上产生很大的影响, 能达到10-100倍甚至更多。
确认提交间隔是所预期的(通常是Spring批处理作业的100-1000倍)。参数配置错误的情况时有发生。
如果某些数据被确定为合格缓存,那么看看这篇博客如何设置Hibernate缓存的:Pitfalls of the Hibernate Second-Level / Query Caches
为了解决应用程序性能问题,最重要的操作是收集一些指标来找到当前的瓶颈是什么。
不给定指标的话几乎不可能在有意义的时间内发现是什么导致问题发生的。
同时,许多但并非所有 ‘数据库-驱动’ 的表现缺陷可以避免在最先使用的Spring Batch框架的应用中发生。
标签:des style blog http color java os 使用 io
原文地址:http://www.cnblogs.com/Lightning-Kid/p/3933679.html