巨大なテーブルからすべてのレコードを取得するときに OOM (メモリ不足) エラーを回避するにはどうすればよいですか?
-
12-09-2019 - |
質問
巨大なテーブルをカスタム XML ファイルに変換するタスクが与えられました。この仕事には Java を使用します。
単純に「SELECT * FROM customer」を発行すると、大量のデータが返され、最終的に OOM が発生する可能性があります。レコードが利用可能になったらすぐに処理し、その後 SQL 取得プロセス中にメモリからレコードを削除する方法はあるでしょうか。
--- 2009 年 7 月 13 日に編集
私の質問を詳しく説明させてください。1 つのデータベースサーバーと 1 つのアプリケーションサーバーがあります。アプリケーションで選択クエリを発行すると、データはデータベースサーバーからアプリサーバーに転送されます。
ResultSet はクエリ内のすべてのレコードを受信するまで待機する必要があると思います (間違っている場合は修正してください)。1000 レコードのテーブルの場合、フェッチ サイズを 4 に設定したとしても、アプリ サーバーのヒープ メモリには 1000 レコードが残ることになりますが、正しいですか?フェッチ サイズは、データベース サーバーとの間の往復回数にのみ影響します。
私の質問は、アプリサーバーに到着した直後にその4つ(または任意の数)のレコードの処理を開始し、それを破棄してアプリサーバーのメモリを解放するにはどうすればよいですか?
解決
私はあなたのこの1と同じソリューションを使用することができると思いますに。スクロール可能な結果セットます。
他のヒント
もう少し情報を使用して、私はより有用な答えを得ることができます。
MySQLを使用している場合:
stmt = conn.createStatement(java.sql.ResultSet.TYPE_FORWARD_ONLY,
java.sql.ResultSet.CONCUR_READ_ONLY);
stmt.setFetchSize(Integer.MIN_VALUE);
http://www.oracle.com/から技術/ハイテク/ javaの/ sqlj_jdbc / htdocsに/ jdbc_faq.htmlするます:
java.util.Properties info = new java.util.Properties();
info.put ("user", "scott");
info.put ("password","tiger");
info.put ("defaultRowPrefetch","15");
getConnection ("jdbc:oracle:oci:@",info);
あなたは一度に一つのレコードを反復カーソルでResultSetを使用することができます。あなたは一度ではなくXMLを構築するためにDOMを使用するよりも、ファイル1つのレコードにあなたのXMLを書き出すことを、次に確認しますする必要があります。
私は私の経験から学んだ親指の一つのルールは、あなたのアプリケーションサーバにデータベースからすべてのデータを持ってないことです。あなたができることの一つは、あなたのデータページに手順を実装している。
あなたは再び、次のページのデータをフェッチ、それらを処理、1000から5000回の記録を中心に含むデータの1ページをもたらすことができます。
テーブル全体をエクスポートするための概念。 (専門家への注意:私はその欠点を承知しています。)
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
public class FullTableExport {
public static String toXML(String s) {
if (s != null) {
StringBuilder b = new StringBuilder(s.length());
for (int i = 0, count = s.length(); i < count; i++) {
char c = s.charAt(i);
switch (c) {
case '<':
b.append("<");
break;
case '>':
b.append(">");
break;
case '\'':
b.append("'");
break;
case '"':
b.append(""");
break;
case '&':
b.append("&");
break;
default:
b.append(c);
}
}
return b.toString();
}
return "";
}
public static void main(String[] args) throws Exception {
String table = "CUSTOMER";
int batch = 100;
Class.forName("oracle.jdbc.driver.OracleDriver");
Connection conn = DriverManager.getConnection(
"jdbc:oracle:thin:@server:orcl", "user", "pass");
PreparedStatement pstmt = conn.prepareStatement(
"SELECT /*+FIRST_ROWS(" + batch + ") */ * FROM " + table);
ResultSet rs = pstmt.executeQuery();
rs.setFetchSize(batch);
ResultSetMetaData rsm = rs.getMetaData();
File output = new File("result.xml");
PrintWriter out = new PrintWriter(new BufferedWriter(
new OutputStreamWriter(
new FileOutputStream(output), "UTF-8")), false);
out.printf("<?xml version='1.0' encoding='UTF-8'?>%n");
out.printf("<table name='%s'>%n", toXML(table));
int j = 1;
while (rs.next()) {
out.printf("\t<row id='%d'>%n", j++);
for (int i = 1; i <= rsm.getColumnCount(); i++) {
out.printf("\t\t<col name='%s'>%s</col>%n",
toXML(rsm.getColumnName(i)),
toXML(rs.getString(i)));
}
out.printf("\t</row>%n");
}
out.printf("</table>%n", table);
out.flush();
}
}
編集 欠点 (@J.S. に感謝):
- ojdbc 以外の外部ライブラリは使用されません
- 何も閉まってないよ
- 一般的な例外がスローされる
- 主な方法です
- XML 生成のための print の使用法
- Oracle 固有の SQL
- プレーンテキストのパスワード
- 一部の列は文字列表現が不自然に見えます
- UTF-8は国際的すぎる
- XML 構造のフットプリントが大きい
OOMエラーが発生している段階では、XMLファイルへのデータの検索や処理データにある
そのデータ検索は、バッチでデータを取得する場合。 、最初の行の合計数を取得し、主キーによって選択を注文し、チュアブルサイズに選択された行を制限します。
そのXMLファイルを作成する時に、System.out.printlnは、各顧客のXMLノードを送信する場合は、、メモリに保持しないでください。 commad回線を介してプログラムを起動し、ファイルへのすべての出力をリダイレクトする;
java MyConverter > results.txt
すべてがファイルに保存されたレコードを紹介ループとしてます。