【发布时间】:2018-08-16 15:18:18
【问题描述】:
我当前的项目包括一个归档功能,其中将内存数据库中的数据传输到关系数据库。
我从内存数据库中流式传输结果,创建休眠实体并将数据以 5000 个批次保存到数据库中。这些实体有几个关系,因此我将每个实体写入不同的表。
作为参考,您可以假设在整个归档过程中执行了 100 万次插入查询。
一开始这个过程真的很慢,于是上网查了一下,实现了一些用Hibernate批量写的常用建议:
- 我将 hibernate.jdbc.batch_size 设置为合适的大小,并将 hibernate.order_inserts 设置为 true。
- 为防止出现内存问题,我不时刷新并清除休眠会话。
这是一个批处理的小例子:
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 次查询。
目前,整个归档功能都包含在 1 个事务中,这感觉完全不对。最大的好处是您可以确定存档是否成功完成,而无需为此提供一些额外的检查系统。 (一切要么在数据库中,要么不在)
作为加速实验,我修改了代码,为每个实体块 (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 合规性会导致开销,但我没想到会出现如此巨大的速度差异。
【问题讨论】: