从数据库中读取 +800 万条记录的内存泄漏

Raz*_*192 3 java mysql sql memory-leaks

我有一个包含 800 万条记录的数据库,我需要以特定方式处理这些记录,这些记录是用 Java 编写的。在查找了一些东西后,我发现了以下相关帖子:

这是我的代码,它返回存储在我的 MySQL 数据库的标签列中的项目:

public ResultSet getAllTags() {
    String query = "SELECT Tags FROM dataset";
    ResultSet rs = null;

    try {
        connection = ConnectionFactory.getConnection(DATABASE);
        preparedStatement = connection.prepareStatement(query, ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_READ_ONLY);
        preparedStatement.setFetchSize(Integer.MIN_VALUE);
        rs = preparedStatement.executeQuery(query);
        // following line is for testing, to see what comes out of the resultset
        System.out.println("output: " + rs.getString(1));
        return rs;
    } catch (Exception ex) {
        ex.printStackTrace();
        return null;
    } finally {
        closeAll();
    }
}
Run Code Online (Sandbox Code Playgroud)

在这里,我返回 ResultSet 以便我处理rs.next()循环中的每一行。然而,rs = preparedStatement.executeQuery(query);它开始吃掉我计算机的所有可用内存(我在 Mac OSX 上工作,内存为 8GB。只有 Eclipse 打开时,我还有 +/- 5GB 可用空间,当运行应用程序时,它会下降到 < 100MB 可用空间)让我关闭数据库连接和应用程序等...所以我认为这可以称为内存泄漏?

有人可以解释我做错了什么,以及为什么即使我按照其他具有类似记录数量的页面上的说明也会出现此问题?

JB *_*zet 5

您唯一做错的是使用愚蠢的数据库驱动程序 (MySQL),默认情况下,它会读取内存中的整个结果集。

尝试使用http://dev.mysql.com/doc/connector-j/en/connector-j-reference-configuration-properties.html 中描述的 useCursorFetch 和 defaultFetchSize 属性来避免这种情况,您应该能够遍历行而不获取内存中的所有内容(虽然未测试)。

请注意,该行

System.out.println("output: " + rs.getString(1));
Run Code Online (Sandbox Code Playgroud)

将抛出异常,因为您next()尚未在结果集中调用。另请注意,如果closeAll()关闭连接,调用者将无法遍历结果集,因为它将被关闭。您应该在关闭连接之前执行迭代。

请注意,驱动程序的文档说:

默认情况下,ResultSet 被完全检索并存储在内存中。在大多数情况下,这是最有效的操作方式,并且由于 MySQL 网络协议的设计更容易实现。如果您正在使用具有大量行或大值的 ResultSet,并且无法在 JVM 中为所需内存分配堆空间,则可以告诉驱动程序一次将结果流回一行。

要启用此功能,请按以下方式创建 Statement 实例:

stmt = conn.createStatement(java.sql.ResultSet.TYPE_FORWARD_ONLY,
          java.sql.ResultSet.CONCUR_READ_ONLY);
stmt.setFetchSize(Integer.MIN_VALUE);
Run Code Online (Sandbox Code Playgroud)

但你已经习惯了,TYPE_SCROLL_SENSITIVE而不是TYPE_FORWARD_ONLY