编写数据加载器的最佳方法是什么?

时间:2013-07-26 14:51:39

标签: java spring hibernate

我正在使用Spring 2.5以及随之而来的Hibernate。我正在运行Oracle 11g数据库。

我创建了扩展HibernateTemplate的DAO。现在我想编写一个在我的person表中插入500万行的加载器。我用简单的方式写了这个,比如从CSV文件中读取一行,把它变成一个人,保存到表格中。继续这样做,直到CSV文件为空。

问题是我用尽了大约450000行的堆空间。所以我将内存的大小从1024m扩大到2048m,现在我在大约900000行之后耗尽了内存。

... Hmmmmm

所以我已经阅读了一些关于关闭Hibernate查询缓存的事情,但我没有使用L2缓存,所以我认为这不是问题。

我已经阅读了一些有关JDBC2批处理的内容,但我认为这不适用于hibernate。

所以,我想知道是否有一个关于Hibernate的基本问题我不知道。

3 个答案:

答案 0 :(得分:2)

根据我使用EclipseLink的经验,在插入/更新许多记录时保持单个事务处于打开状态会导致您遇到的症状。

您正在使用EntityManager(某种,JPA或Hybernate特定的 - 它仍在管理Entity)。它试图将工作集保留在内存中,用于事务的生命周期。

一般解决方案是提交&每N次插入后重启事务;对我来说典型的N是1000。


作为脚注,对于EclipseLink的某些版本(未定义,已经过几年),会话刷新/清除并没有解决问题。

答案 1 :(得分:2)

老实说,我不会使用hibernate。 ORM不是为了将数百万行加载到DB中而设计的。不是说你不能,但有点像用电钻挖一个游泳池;你会用挖掘机,而不是钻头。

在您的情况下,我会使用数据库附带的加载器应用程序将CSV直接加载到DB。如果您不想这样做,是的,批量插入将更有效。我不认为Hibernate会让你轻松地做到这一点。如果我是你,我只使用普通的JDBC,或者最多使用Spring JDBC。

如果你在实体中有复杂的businesslogic并且绝对必须使用Hibernate,那么你可以像Richard建议的那样刷新每个N条记录。但是,我认为这是一个非常糟糕的黑客。

答案 2 :(得分:0)

由于你的第一级缓存(Hibernate会话),听起来你的空间不足。您可以定期刷新Hibernate会话以降低内存使用率,并通过提交每几千行来将工作分解为块,从而防止数据库的事务日志变得太大。

但是使用Hibernate来执行这样的加载任务会很慢,因为JDBC很慢。如果你很清楚环境会是什么样的,你就可以限制数据量,并且你有一个足够大的窗口可以进行处理,然后你可以进行管理,但是在你希望它可以在多个工作的情况下不同的客户端站点,并且您希望最小化由于某些客户端站点的加载作业无法解决问题所花费的时间,那么您应该使用数据库的批量复制工具。

批量复制方法意味着数据库暂停所有约束检查以及索引构建和事务日志记录,而不是集中于尽可能快地篡改数据。因为JDBC无法从数据库中获得这种级别的合作,所以它无法与之竞争。在之前的工作中,我们更换了一个JDBC加载器任务,该任务花费了8个多小时来运行,花费了20分钟的SQLLoader任务。

您确实牺牲了数据库独立性,但所有数据库都有批量复制工具(因为DBA依赖它们),因此您将为每个数据库创建一个非常类似的进程,只有您调用的exe和指定文件格式的方式应该改变。这样您就可以充分利用处理窗口。