为什么在更大的事务中,使用Hibernate在Oracle上批量插入数据要慢得多?

时间:2018-03-08 09:58:40

标签: java oracle hibernate

我当前的项目包括一个归档功能,其中来自内存数据库的数据被传输到关系数据库。

我从内存数据库中流式传输结果,创建hibernate实体并将数据持久保存到数据库5000.这些实体有几个关系,所以每个实体写入不同的表。

作为参考,您可以假设在整个归档过程中执行了100万次插入查询。

这个过程在开始时非常慢,所以我在线查看并实现了一些与Hibernate批量编写的常见建议:

  • 我将hibernate.jdbc.batch_size设置为一个好的大小,将hibernate.order_inserts设置为true。
  • 为了防止内存问题,我不时地刷新并清除hibernate会话。

以下是批处理的一个小例子:

RedisServiceImpl.Cursor<Contract> ctrCursor = contractAccessService.getCursor("*", taskId);

Iterators.partition(ctrCursor, BATCH_SIZE).forEachRemaining(chunk -> {

    portfolioChunkSaver.saveContractChunk(chunk, taskId);

    em.flush();
    em.clear();
});

ctrCursor.close();

这个过程有效,但速度非常慢。在Oracle中插入100万条记录需要大约2个小时才能完成,即每秒约2.5次查询。

目前,整个归档功能都包含在一个事务中,这根本感觉不对。最大的好处是,您可以确定存档是否成功完成,而无需为此提供一些额外的检查系统。 (一切都在数据库中,或者它不是

作为一个加速实验,我修改了代码,以便为每个实体块创建一个数据库事务(5000),而不是将所有内容包装在一个大事务中。

这种变化产生了巨大的影响,现在的速度比以前快了大约10-15倍。

分析时,我在更改之前看到了这种行为:

Before: 
Java - very low CPU
Oracle - very high CPU, low disk write activity

After:
Java - high CPU
Oracle - Low CPU, very high disk write activity

第二种行为很有意义,java发送尽可能多的查询,数据库服务器受到本地系统写入磁盘速度的限制。

我的问题出现了:为什么影响如此巨大?当我在更大的交易中发送所有内容时,Oracle的做法有何不同?

作为旁注:我从未遇到过MySQL的这个问题,所以Oracle(或oracle JDBC驱动程序)必须以不同的方式做某事。

我可以想象保证ACID合规性会导致开销,但我不会期望这种巨大的速度差异。

1 个答案:

答案 0 :(得分:0)

你应该确保你有足够的UNDO空间(也称为UNDO段),因为大型事务会占用大量的空间。

  

发出ROLLBACK语句时,撤消记录用于撤消   未提交的事务对数据库所做的更改。

只有在您完成数据完整性时才提交,并且经过适当调整的Oracle数据库可以支持大型事务而不会出现任何性能问题。