拥有一个需要处理的带有+8百万条记录的MySQL数据库(这在数据库本身无法完成),在尝试将它们读入我的Java应用程序时会遇到问题。
我已经尝试过一些有类似问题的人的解决方案(例如,link)但是,没有一个能为我解决问题。我试着设置FetchSize和所有,但没有运气!我的应用程序是使用BlockingQueue构建的,Producer从数据库中连续读取数据,将其存储在队列中,以便Consumer可以处理它。这样我就可以同时限制主内存中的记录数量。
我的代码适用于少量记录(我测试了1000条记录)所以我建议从数据库到我的应用程序的fase需要修复。
EDIT1
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();
EDIT2
最终,除了看到我的记忆力下降之外,我得到了一些输出。我收到这个错误:
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)
EDIT3
我围绕Producer-Consumer模式做了一些研究,结果发现,当Consumer无法跟上Producer时,队列会自动放大,最终耗尽内存。所以我切换到ArrayBlockingQueue,这使得大小固定。但是,我仍然得到记忆。 Eclipse Memory Analyzer说ArrayBlockingQueue占用了我内存的65.31%,而内存中只有1000个对象,所有文本都有4个字段。
答案 0 :(得分:4)
您需要流式传输结果。使用MySQL驱动程序,您必须为CONCUR_READ_ONLY
设置TYPE_FORWARD_ONLY
和ResultSet
。另外,相应地设置提取大小: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,作为驱动程序逐行传输结果集的信号。在此之后,将逐行检索使用该语句创建的任何结果集。
这种做法有一些警告......
答案 1 :(得分:0)
为什么不为此解决方案尝试此方法
Problem exporting a lot of data from database to .csv with java
除了获取整个结果集之外,它可以逐个获取,然后可以用于处理。链接我所指的你一个接一个地获取记录并写入文件,但你可以使用这个结果进行处理。这是你可以使用的一种方法。
另一种方法是你可以根据你的需求获取记录的多线程概念,并将单独处理。