【问题标题】:Reading large amount of records MySQL into Java将大量记录MySQL读入Java
【发布时间】:2014-03-17 11:03:43
【问题描述】:

拥有一个包含 +800 万条记录的 MySQL 数据库,我需要处理(这在数据库本身中无法完成),我在尝试将它们读入我的 Java 应用程序时遇到问题。

我已经尝试了一些有类似问题的人的解决方案(例如,link),但是没有一个适合我。我试图设置 FetchSize 和所有,但没有运气!我的应用程序是使用 BlockingQueue 构建的,生产者从数据库中连续读取数据,将其存储在队列中,以便消费者可以处理它。这样我可以同时限制主内存中的记录数量。

我的代码适用于少量记录(我测试了 1000 条记录),所以我建议需要修复从数据库到我的应用程序的 fase。

编辑1

connection = ConnectionFactory.getConnection(DATABASE);
preparedStatement = connection.prepareStatement(query, java.sql.ResultSet.CONCUR_READ_ONLY, java.sql.ResultSet.TYPE_FORWARD_ONLY);
preparedStatement.setFetchSize(1000); 
preparedStatement.executeQuery();
rs = preparedStatement.getResultSet();

编辑2

最终,除了看到我的记忆力下降之外,我得到了一些输出。我收到此错误:

Exception in thread "Thread-0" java.lang.OutOfMemoryError: Java heap space
at com.mysql.jdbc.Buffer.<init>(Buffer.java:59)
at com.mysql.jdbc.MysqlIO.nextRow(MysqlIO.java:2089)
at com.mysql.jdbc.MysqlIO.readSingleRowSet(MysqlIO.java:3554)
at com.mysql.jdbc.MysqlIO.getResultSet(MysqlIO.java:491)
at com.mysql.jdbc.MysqlIO.readResultsForQueryOrUpdate(MysqlIO.java:3245)
at com.mysql.jdbc.MysqlIO.readAllResults(MysqlIO.java:2413)
at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2836)
at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2828)
at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2777)
at com.mysql.jdbc.StatementImpl.executeQuery(StatementImpl.java:1651)
at razoralliance.dao.DataDAOImpl.getAllDataRS(DataDAOImpl.java:38)
at razoralliance.app.DataProducer.run(DataProducer.java:34)
at java.lang.Thread.run(Thread.java:722)

编辑3

我对 Producer-Consumer 模式做了一些研究,结果发现,当 Consumer 跟不上 Producer 时,队列会自动扩大,最终导致内存耗尽。所以我切换到 ArrayBlockingQueue ,它使大小固定。但是,我仍然会出现内存泄漏。 Eclipse Memory Analyzer 说 ArrayBlockingQueue 占用了我 65,31% 的内存,而它在内存中只有 1000 个对象,其中 4 个字段都是文本。

【问题讨论】:

  • 您能否详细说明该处理的作用?
  • @fge,这是我需要进行文本分类的项目。所以在数据库中是我数据的原始表示。通过处理,我必须删除无用的信息以改进文本分类阶段。所以删除停用词,标记化,...
  • 您是否调用了 Statement.setMaxRows(0) 以使 ResultSet 没有限制?
  • 你没有得到任何错误或异常,发布它们!
  • Fetch 大小对内存消耗没有影响,它只影响执行的网络往返次数。

标签: java mysql database


【解决方案1】:

您需要流式传输您的结果。使用 MySQL 驱动程序,您似乎必须为您的ResultSet 设置CONCUR_READ_ONLYTYPE_FORWARD_ONLY。另外,相应地设置提取大小:stmt.setFetchSize(Integer.MIN_VALUE);

默认情况下,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);

只进、只读结果集的组合,提取大小为 Integer.MIN_VALUE,用作驱动程序逐行流式传输结果集的信号。在此之后,将逐行检索使用该语句创建的任何结果集。

这种方法有一些注意事项...

【讨论】:

  • 你可以在我添加的代码sn-p中看到,我已经使用了,对吧?
  • @RazorAlliance192 不在最初的问题中,您仍然缺少stmt.setFetchSize(Integer.MIN_VALUE);
  • 对不起,先生!我在想 Integer.MIN_VALUE 只是一个占位符,表示我可以选择的一些数字(例如,每次只提取 1000 行)
  • 这种方法还有哪些其他限制或注意事项?流媒体效率低吗?请编辑您的答案,以更清楚地解释这种方法的好处/缺陷。
【解决方案2】:

你为什么不试试这个解决方案的方法

Problem exporting a lot of data from database to .csv with java

可以一个一个地获取而不是获取整个结果集,然后可以将其用于处理。我所指的链接用于逐一获取记录并写入文件,但您可以使用此结果进行处理。这是您可以使用的一种方法。

另一种方法是您可以使用多线程概念,该概念将根据您的需求获取记录并单独处理。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-05-14
    • 2013-08-18
    • 1970-01-01
    • 2012-09-24
    • 1970-01-01
    • 2013-06-03
    相关资源
    最近更新 更多