代码之家  ›  专栏  ›  技术社区  ›  janetsmith

从大表中检索所有记录时,如何避免OOM(内存不足)错误?

  •  15
  • janetsmith  · 技术社区  · 15 年前

    我有一个任务要将一个大表转换为自定义XML文件。我将使用Java进行这项工作。

    如果我只是发出一个“select*from customer”,它可能会返回大量最终导致OOM的数据。我想知道,有没有一种方法可以在记录可用时立即处理它,然后在SQL检索过程中从内存中删除记录?

    ——2009年7月13日编辑

    让我详细阐述一下我的问题。我有1个数据库服务器和1个应用程序服务器。 当我在应用程序中发出选择查询时,数据将从数据库服务器传输到应用服务器。

    我相信(如果我错了,请纠正我)resultset需要等待,直到接收到查询中的所有记录。即使我们将FETCH SIZE设置为4,对于一个1000记录表,在应用服务器的堆内存中仍然有1000条记录,对吗?fetch大小只影响从/到DB服务器的往返次数。

    我的问题是,如何在4条(或任何数字)记录到达应用服务器后立即开始处理它,并处理它以释放应用服务器中的内存?

    6 回复  |  直到 11 年前
        1
  •  4
  •   Community CDub    7 年前

    我想你可以用和 this one . 可滚动的结果集。

        2
  •  6
  •   Clint    15 年前

    有了更多的信息,我可以得到一个更有用的答案。

    如果您使用的是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);
    
        3
  •  4
  •   Dean Povey    15 年前

    如果您使用的是JDBC,那么可以使用一个带有光标的结果集,一次遍历一个记录。然后,您需要确保将XML一次写入一个文件,而不是使用DOM来构建XML。

        4
  •  4
  •   Kirtan    15 年前

    从我的经验中我学到的一个经验法则是,永远不要将数据库中的所有数据都带到应用服务器上。您可以做的一件事是实现一个过程来分页您的数据。

    您可以携带一页包含大约1000-5000条记录的数据,对其进行处理,然后再次获取下一页的数据。

        5
  •  1
  •   akarnokd    15 年前

    导出整个表的概念。 (专家注意:我知道它的缺点。)

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

    编辑 缺点(谢谢@J.S.):

    • 除了ojdbc之外没有使用任何外部库
    • 什么都没有关闭
    • 引发了一般异常
    • 这是一个主要的方法
    • 打印用于XML生成
    • Oracle特定的SQL
    • 纯文本密码
    • 有些列在字符串表示中看起来很笨拙
    • UTF-8太国际化了
    • XML结构占用空间很大
        6
  •  0
  •   n002213f    15 年前

    OOM错误发生在哪个阶段,是在数据检索还是在将数据处理为XML文件?

    如果是数据检索,则批量获取数据。首先获取行的总数,按主键对所选内容排序,并将所选行限制为可咀嚼的大小。

    如果是在创建XML文件时,请将每个客户的XML节点发送到system.out.println,不要将其保存在内存中。通过命令行启动程序,并将所有输出重定向到一个文件;

    java MyConverter > results.txt
    

    当您遍历记录时,所有记录都保存在文件中。

    推荐文章