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

标签 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();
    }
}

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

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

最佳答案

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

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

注意这一行

System.out.println("output: " + rs.getString(1));

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

请注意 the documentation司机说:

By default, ResultSets are completely retrieved and stored in memory. In most cases this is the most efficient way to operate, and due to the design of the MySQL network protocol is easier to implement. If you are working with ResultSets that have a large number of rows or large values, and cannot allocate heap space in your JVM for the memory required, you can tell the driver to stream the results back one row at a time.

To enable this functionality, create a Statement instance in the following manner:

stmt = conn.createStatement(java.sql.ResultSet.TYPE_FORWARD_ONLY,
          java.sql.ResultSet.CONCUR_READ_ONLY);
stmt.setFetchSize(Integer.MIN_VALUE);

但您使用的是 TYPE_SCROLL_SENSITIVE 而不是 TYPE_FORWARD_ONLY

关于java - 从数据库中读取 +800 万条记录的内存泄漏,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22577433/

相关文章:

sql - Valentina Studio 插入了数百万行

java - Log4J 动态配置

MySQL 查询 - 在 UPDATE 中使用 SELECT

Android Room - 嵌套对象模型更改时如何迁移?

C# MySQL 将相同的数据插入到 2 个不同的表中

php - php中的Mysql更新不会更新错误500

java - 如何合并2个java程序的输出?

java - 如何使用二维数组存储坐标?

java - 指向子类对象的父类(super class) ref 的类类型是什么?

python - 我需要什么才能在我的浏览器中成功运行一个执行 Python 脚本的网站?