我要遍历一个有1500w条记录的数据表,对其中的每条记录进行修改,然后再存回数据库中去。
下面是我写的代码,一运行就会抛内存溢出。(省略部分与问题无关代码)
[code="java"]
Connection conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/db?useCursorFetch=true", "user","pwd");
stmt = conn.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_UPDATABLE);
stmt.setFetchSize(5000);
rs = stmt.executeQuery("select * from table");
while (rs.next())
{
// 处理记录(处理过程只能用程序写)
...
// 更新记录
rs.updateString(col_index, "处理后的值");
...
rs.updateRow();
}
[/code]
下面是抛出的异常,在stmt.executeQuery()那一句就抛出了:
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at com.mysql.jdbc.MysqlIO.nextRowFast(MysqlIO.java:1574)
at com.mysql.jdbc.MysqlIO.nextRow(MysqlIO.java:1398)
at com.mysql.jdbc.MysqlIO.readSingleRowSet(MysqlIO.java:2816)
at com.mysql.jdbc.MysqlIO.getResultSet(MysqlIO.java:467)
at com.mysql.jdbc.MysqlIO.readResultsForQueryOrUpdate(MysqlIO.java:2510)
at com.mysql.jdbc.MysqlIO.readAllResults(MysqlIO.java:1746)
at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2135)
at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2536)
at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2465)
at com.mysql.jdbc.StatementImpl.executeQuery(StatementImpl.java:1383)
at com.wawame.geocoding.Test.main(Test.java:37)
最后说明:这个过程必须要用程序来写。
还请各位大能帮我看一下。
先谢谢了!
[b]问题补充:[/b]
感谢各位的回答,分页处理一开始是很快的,但是到后面会很慢,比如limit 10000000, 10000的时候单查询就慢死了。
现在我在试按主键来分批处理,这个还在测试中
1500W条数据全部载入内存,数据量太大了。
如果一条数据只有代理主键字段,定义成int类型.那么就是57M多.你可以大概算下一条数据量是int数据量的几倍.
解决办法:内存溢出,增大内存是最直接的办法.
另一种办法是,重复利用内存,分页查询出数据,设定一页的条数,查询一页,处理一页,处理完了然后再查询,重复这个过程,直至1500W条完全处理结束.
弱弱的问一句,难道不能分页处理么,把每次处理的记录设置小一点,这样就不会出错了 :lol:
java虚拟机内存设置小了,内存容纳不了5000条数据。在jvm启动项里面设置内存的大小。
希望能解决哥们的问题。
这个不好解决了,,最好是通过优化sql语句解决,,,可以分开查,或者去控制游标查询。
[code="java"]sql = update table a set a.col_index = ? where a.condition = ?
PreparedStatement ps = con.prepareStatement(sql);
for(int i = 0; i < 1500W ;i++){
for(int j = 0 ; j < 2000; j++){
ps.setString(col_index, "处理后的值");
ps.addBatch();
}
con.commit();
}[/code]
大概就这个意思
ps = (PreparedStatement) con.prepareStatement("select * from bigTable",
ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
ps.setFetchSize(Integer.MIN_VALUE);
ps.setFetchDirection(ResultSet.FETCH_REVERSE);
1、当statement设置以下属性时,采用的是流数据接收方式,每次只从服务器接收部份数据,直到所有数据处理完毕,不会发生JVM OOM。
setResultSetType(ResultSet.TYPE_FORWARD_ONLY);
setFetchSize(Integer.MIN_VALUE);
2、调用statement的enableStreamingResults方法,实际上enableStreamingResults方法内部封装的就是第1种方式。
3、设置连接属性useCursorFetch=true (5.0版驱动开始支持),statement以TYPE_FORWARD_ONLY打开,再设置fetch size参数,表示采用服务器端游标,每次从服务器取fetch_size条数据。