2009-07-10 1 views
15

거대한 테이블을 사용자 정의 XML 파일로 변환하는 작업이 있습니다. 나는이 일을 위해 자바를 사용할 것이다.거대한 테이블에서 모든 레코드를 검색 할 때 OOM (메모리 부족) 오류를 방지하는 방법?

단순히 "SELECT * FROM customer"를 발행하면 엄청난 양의 데이터가 반환되어 결국 OOM이 발생할 수 있습니다. 내가 궁금해, 내가 레코드를 즉시 사용할 수있게 처리 할 수있는 방법, 거기에 메모리에서 프로세스를 검색하는 동안 그 후에 레코드를 제거하는 것입니다 궁금해?

--- 2009 년 7 월 13 일 일요일 수정 됨

내 질문에 대해 자세히 설명해 드리겠습니다. 1 db 서버와 1 응용 프로그램 서버가 있습니다. 응용 프로그램에서 select 쿼리를 실행하면 데이터가 db 서버에서 응용 프로그램 서버로 이동합니다.

내가 틀렸다고 확신한다. ResultSet은 쿼리의 모든 레코드를받을 때까지 기다려야한다. 페치 크기를 4로 설정하더라도 1000- 레코드 테이블의 경우 응용 프로그램 서버의 힙 메모리에 1000 개의 레코드가 남아있게됩니다. 맞습니까? 가져 오기 크기는 DB 서버에서 /로 왕복하는 횟수에만 영향을줍니다.

제 질문은 앱 서버에 도착한 직후 그 4 개 (또는 모든 숫자) 레코드에 대한 처리를 시작하고 앱 서버에 메모리를 확보하기 위해 처분하는 방법입니다.

+0

너무 국제이다 당신은 (즉 MS, Oracle, MySql 등)? 사용중인 용도에 따라이 문제를 처리하는 방법에는 여러 가지가 있습니다. –

+0

레코드를 검색하는 데 사용하는 코드를 보여주십시오. 당신이 그것들을 반복하거나 컬렉션에 로컬로 저장 한 다음 그것을 반복한다면 그것은 명확하지 않습니다. –

+0

이 작업을 위해 Oracle을 사용할 것입니다. 나는 아직 어떤 코드도 시작하지 않았다. 왜냐하면 나는 여전히 최선의 방법을 연구하고 있기 때문이다. – janetsmith

답변

4

난 당신이 this one과 같은 솔루션을 사용할 수 있습니다 생각합니다. 스크롤 가능한 결과 집합.

+1

감사합니다. 이것은 또한 가능한 해결책이지만,이 방법을 사용할 경우 왕복 당 기록 (페치 크기 = 1)은 1 회로 만입니까? – janetsmith

6

조금 더 많은 정보를 통해보다 유용한 답변을 얻을 수 있습니다.

당신은 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/technology/tech/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); 
+1

Integer.MIN_VALUE? 정말? 그건 음수입니다. 10, 100, 1000 같은 것을 원하지 않니? –

+0

Oracle을 사용하고 있습니다. 귀하의 MySQL 코드는 나에게 좋은 방향을 제공합니다 :) – janetsmith

+0

+1 TYPE_FORWARD_ONLY는이 경우 DB에 대한 좋은 힌트입니다. 그런 다음 JDBC 드라이버가 유용 할 경우 JDBC 드라이버에 달려 있습니다. –

4

JDBC를 사용하는 경우 한 번에 하나의 레코드를 반복하는 커서로 ResultSet을 사용할 수 있습니다. DOM을 사용하여 XML을 작성하는 대신 한 번에 하나의 레코드에 XML을 작성해야합니다.

+1

XML 생성에 대해서는 이전에 생각했습니다. 나는 XML 출력을 위해 StAX를 사용할 것이다. (http://blogger.ziesemer.com/2007/06/xml-generation-in-java.html) – janetsmith

4

내 경험에 비추어 볼 때, 데이터베이스의 모든 데이터를 응용 프로그램 서버로 가져 오지 않는 것이 좋습니다. 한 가지 할 수있는 것은 데이터를 페이지 처리하는 프로 시저를 구현하는 것입니다.

약 1000-5000 개의 레코드가 포함 된 한 페이지의 데이터를 가져 와서 처리 한 다음 다시 다음 페이지의 데이터를 가져올 수 있습니다.

+1

나는 두 번째 옵션이다. 그러나 예제 한계는이 경우 좋지 않습니다. 1000에서 5000으로 크게하십시오. 이미 실행 계획을 사용할 수 있으므로 다시 가져 오는 데 시간이 많이 걸리지는 않습니다. 또한 모든 데이터베이스 서버에서 작동합니다. –

+0

10,000 ~ 50,000 또는 100,000까지도 사용할 수 있다고 생각합니다. 행의 데이터가 얼마나 큰지 잘 모릅니다. 하지만 50,000은 괜찮아 보입니다. 직접 해보십시오. –

+0

@Vinegar : 한도가 수정되었습니다. 사용 가능한 메모리의 양과 테이블의 내용은 모두 다릅니다. 그러나 나는 1000-5000이 더 좋다고 생각합니다. – Kirtan

0

OOM 오류가 발생하는 단계는 데이터 검색 또는 XML 파일에 대한 데이터 처리 중입니까?

데이터를 검색하는 경우 데이터를 일괄 적으로 가져옵니다. 먼저 총 행 수를 가져오고 기본 키로 선택을 정렬하고 선택된 행을 씹을 수있는 크기로 제한하십시오.

XML 파일을 만들 때 각 고객의 XML 노드를 System.out.println으로 보내고 메모리에 저장하지 마십시오. commad 라인을 통해 프로그램을 시작하고 모든 출력을 파일로 리디렉션하십시오.

java MyConverter > results.txt 

레코드를 루프 할 때 모든 내용이 파일에 저장됩니다.

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("&lt;"); 
        break; 
       case '>': 
        b.append("&gt;"); 
        break; 
       case '\'': 
        b.append("&#39;"); 
        break; 
       case '"': 
        b.append("&quot;"); 
        break; 
       case '&': 
        b.append("&amp;"); 
        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(); 
    } 
} 

편집 단점 (감사 @JS) 다음 ojdbc

  • 아무것도 이상 사용

    • 외부 라이브러리가
    • 가 일반적인 예외가 발생 닫혀
    • 주요 방법입니다.
    • XM 용 인쇄물 사용 L 세대
    • 오라클 특정 SQL
    • 일반 텍스트 암호
    • 일부 열을 문자열 표현에 어색
    • UTF-8은
    • XML 구조 풋 프린트는 SQL 서버가 큰
  • +0

    샘플 코드를 게시하고 단점을 알고 있다면 최소한 언급해야합니다. 아시다시피 예제 코드 *는 프로덕션 시스템에 표시됩니다. –

    관련 문제