【问题标题】:Building dynamic query for large files为大文件构建动态查询
【发布时间】:2017-10-30 14:26:43
【问题描述】:

我正在尝试加载一个大文本文件(介于 400-800MB 之间)以及我想将记录插入数据库的文件,但是我遇到了性能问题和内存问题(不是足够的堆空间)。我想知道我目前正在做的事情是否有更好的方法。

所以我正在加载的文本文件具有简单的格式,它会是这样的:

00000  Andy   8920  N  UNL  ...
00001  Roger  4428  N  TRX  ...
,,, 

当前方法:读取每一行,获取字段,然后构建查询

ArrayList<ArrayList<String>> fields = ArrayList<ArrayList<String>>();
ArrayList<String> data= new ArrayList<String>();
while ((line = br.readLine()) != null) {
    if(line.length() >= 6)
        data.add(line.substring(0, 6)); 
    if(line.length() >= 24)
        data.add(line.substring(6, 15));  
    if(line.length() >= 30)
        data.add(line.substring(15, 20)); 
    if(line.length() >= 48)
        data.add(line.substring(20, 25));
...
    fields.add(data); //it looks like [[00000, Andy   , 8920,..],[00001, Roger, ...]]
} //end read
System.gc();
db.insertValues(input);

数据库代码

public void insertValues(ArrayList<ArrayList<String>> data) {
        PreparedStatement ps = null;
        Connection con = null;
        try {
            con = getConnection();
            ps = con.prepareStatement("Insert into CUST_ACCT "
                    + "(CID,NAME,R_NUM,CKM_IND,DATE_1,DATE_2,DATE_3,DATE_4,DATE_5,DATE_6,DATE_7,DATE_8,DATE_9,DATE_10,NUMBER_1,NUMBER_2,NUMBER_3,NUMBER_4,NUMBER_5,NUMBER_6,NUMBER_7,NUMBER_8,NUMBER_9,NUMBER_10,STRING_1,STRING_2,STRING_3,STRING_4,STRING_5,STRING_6,STRING_7,STRING_8,STRING_9,STRING_10,GUID,PARN_GUID,LAST_UPDT_DATE_TIME_STAMP)"
                    + " values "
                    + "(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,sysdate)");
for(int i=0; i< data.size(); i++) {
                ps.setString(1, data.get(i).get(0)); //0
                ps.setString(2, data.get(i).get(1)); //1
                ps.setString(3, data.get(i).get(2)); //2
                ps.setString(4, data.get(i).get(3)); //3
        ...
        ps.addBatch();
        }
        int[] i = ps.executeBatch();
        log.info("total of record inserted: "+i.length);
    }

但是,我收到带有 Not enough heap space 错误的 _e_rror 并且我也尝试构建查询,但是它会一个接一个地插入记录,一个小时后它只会插入大约 20k 个记录百万。有没有更好的方法来加载数据?

【问题讨论】:

  • 你不应该把整个内容放在内存中,按块读取文件(例如100行),然后在每个读取块之后使用批量插入。
  • 我很确定您的 DBMS 中有针对此类文件的自动导入功能。
  • 正如@Berger 所说,创建合理的批处理(每 100 或 1000 行,执行批处理并开始下一个)。然后,中型 Oracle 服务器应该能够每秒导入 100 ... 1000 行这样的行。
  • 虽然我同意文件大小是问题的一部分,但我认为问题也是 arraylist 中的 arraylist 的一部分,当我使用整个文件大小构建动态查询时,如果我增加堆大小,但是添加arraylist的arraylist会立即导致它出堆。

标签: java sql oracle performance


【解决方案1】:

你将所有文件加载到内存中,然后尝试逐行读取所有文件,这会导致性能和内存问题(堆空间等...)

您可以使用Scanner 读取文件,这样可以逐行读取而不加载到内存中。

FileInputStream inputStream = null;
Scanner sc = null;
try {
    inputStream = new FileInputStream(path);
    sc = new Scanner(inputStream, "UTF-8");
    while (sc.hasNextLine()) {
        String line = sc.nextLine();
        // db insert!
    }
    if (sc.ioException() != null) {
        throw sc.ioException();
    }
} finally {
    if (inputStream != null) {
        inputStream.close();
    }
    if (sc != null) {
        sc.close();
    }
}

否则使用 Apache Commons IO

LineIterator it = FileUtils.lineIterator(theFile, "UTF-8");
try {
    while (it.hasNext()) {
        String line = it.nextLine();
        // do something with line
        // db insert
    }
} finally {
    LineIterator.closeQuietly(it);
}

为了提高性能,我建议你只打开一次连接

   // your logic....
   Connection con = getConnection();
   // reading file logic
   while (it.hasNext()) {
        String line = it.nextLine();
        // do something with line
        insertValues(con, line);
        // other logic
   }
   // checking exception etc
   } finally {
        if (inputStream != null) {
            inputStream.close();
        }
        if (sc != null) {
            sc.close();
        }

        if (con != null ) {
            con.close();
        }

    }

总结一下:

  1. 逐行读取文件而不加载到内存中
  2. 只打开一次连接(或几次,而不是每次插入)。
  3. 将连接对象传递给您的插入方法
  4. 完成后关闭所有内容。

希望您理解...这些只是简单的示例,您需要根据自己的需要对其进行更改!

【讨论】:

    【解决方案2】:

    不要读取整个文件 - 读取 1000 行,然后使用准备好的语句插入它们并在此之后提交事务。然后再读1000,...

    我还认为 Oracle 有一个特殊的工具来加载数据(google SQL*Loader 和数据泵)。

    【讨论】:

      【解决方案3】:

      让我看看我是否正确理解了您的需求:

      你有一个大文件,文件中的每一行,你需要插入到数据库中的更多表中。我理解对了吗?

      如果是,您是否尝试过使用 Oracle 的 "SQL*Loader" 工具? 我没有为这么大的文件测试它,但它可能是一个解决方案。 您可以从您的 Java 应用程序中调用它。

      【讨论】:

        猜你喜欢
        • 2012-11-23
        • 1970-01-01
        • 2014-07-17
        • 1970-01-01
        • 2019-04-08
        • 2012-12-21
        • 2018-12-21
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多