0

我正在使用 Spring 2.5 和与之配套的 Hibernate。我正在针对 Oracle 11g 数据库运行。

我创建了扩展 HibernateTemplate 的 DAO。现在我想编写一个加载器,在我的 person 表中插入 500 万行。我以一种简单的方式编写了此代码,例如从 CSV 文件中读取一行,将其转换为一个人,然后保存到表格中。继续这样做,直到 CSV 文件为空。

问题是我用完大约 450000 行的堆空间。所以我将内存大小从 1024m 翻倍到 2048m,现在我在大约 900000 行后内存不足。

嗯……

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

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

所以,我想知道我是否缺少关于 Hibernate 的基本知识。

4

3 回答 3

2

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

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

一个通用的解决方案是在每 N 次插入后提交并重新启动事务;对我来说,典型的 N 是 1000。


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

于 2013-07-26T20:45:35.330 回答
2

老实说,我不会为此使用hibernate。ORM 并非旨在将数百万行加载到数据库中。不是说不能,而是有点像用电钻挖游泳池;你会为此使用挖掘机,而不是钻头。

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

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

于 2013-07-26T23:02:52.940 回答
0

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

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

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

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

于 2013-08-06T14:55:23.530 回答