java - 将MySQL大量记录读取到Java中

标签 java mysql database

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

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

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

编辑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

我围绕生产者-消费者模式做了一些更多的研究,结果发现,当消费者跟不上生产者时,队列会自动扩大,最终耗尽内存。所以我切换到 ArrayBlockingQueue 这使得大小固定。但是,我仍然出现内存泄漏。 Eclipse Memory Analyzer 表示 ArrayBlockingQueue 占用了我的 65.31% 内存,而内存中只有 1000 个对象,其中 4 个字段都是文本。

最佳答案

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

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);

The combination of a forward-only, read-only result set, with a fetch size of Integer.MIN_VALUE serves as a signal to the driver to stream result sets row-by-row. After this, any result sets created with the statement will be retrieved row-by-row.

There are some caveats with this approach...

关于java - 将MySQL大量记录读取到Java中,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22452654/

相关文章:

php - 在 PHP PostgreSQL 中转义 SQL 查询

java - Hibernate 表不会自动创建

mysql - MySQL 中的全文搜索不返回任何行

php - Laravel 5.2 IF 语句语法,根据值更改名称

php - 以某种方式计算标签中的条目数?

c# - C#中DataGridView错误列中的数据检索

database - 如何使用 T-SQL 调用 exec(@sql) 来更改当前数据库?

java - Apache httpclient 未知主机问题

java - StringBuffer 未完全读取

java - 特定索引的 Hashmap 替换数组列表