技巧1:使用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’程序,而无需改动一行代码。
技巧2:定期刷新和清理Hibernate会话
当添加/修改数据库的数据时,Hibernate保持了一个已经存在的实体版本的会话,以防在会话关闭之前进行修改。
但是很多时候,一定在数据库中有匹配的插入时我们就可以安全地丢弃实体。这在Java客户机进程中释放了内存,防止长时间运行Hibernate会话所导致的性能问题。
像这样长时间运行的会话应该尽可能被阻止,但是由于某种原因需要它们的话就应该包含内存是如何消耗的:
1
2
|
entityManager.flush();
entityManager.clear();
|
flush
将触发插入新实体从而发送到数据库。clear
则从会话释放新的实体。
技巧3:减少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有详细描述。
技巧4:搜寻 “差的” 查询计划
在最慢的查询列表里进行检查来看他们是否有良好的查询计划。最常见的“差劲的”查询计划是:
- 全表扫描:表完全地被扫描是因为经常缺少索引或者过时的表统计。
- 笛卡尔连接:这意味着几张表进行笛卡儿积后的结果正在进行计算。检查正在丢失的连接条件,或者可以通过将一个步骤分为几步来完成可以避免发生这个问题。
技巧5:检查错误的提交时间间隔
如果你是正在做批处理,那么提交间隔会在性能结果上产生很大的影响, 能达到10-100倍甚至更多。
确认提交间隔是所预期的(通常是Spring批处理作业的100-1000倍)。参数配置错误的情况时有发生。
技巧6:使用二级查询缓存
如果某些数据被确定为合格缓存,那么看看这篇博客如何设置Hibernate缓存的:Pitfalls of the Hibernate Second-Level / Query Caches