批量批量插入MySQL会产生GC Overhead和/或Java Heap Space错误

问题描述 投票:0回答:1

我试图一次插入大约190万行作为批量插入MySQL。该代码的工作方式类似于1.7 mil或更少行的魅力,但我得到的GC Overhead和/或有时Java Heap Space错误超过1.8 Mil行。

码:

    try {
            // triples is of type ArrayList<String>
            String[] tripleToInsert= null;
            for (int i = 0; i < triples.size(); i++) {
                count++;
                tripleToInsert= triples.get(i).split("\\s+");

                /** Insert <s,p,o> into Triples table **/

                preparedStmt.setString(1, tripleToInsert[0].trim());
                preparedStmt.setString(2, tripleToInsert[1].trim());
                preparedStmt.setString(3, tripleToInsert[2].trim());

                preparedStmt.addBatch();
                preparedStmt.clearParameters();

                tripleToInsert=null;
            }


        }
        catch(OutOfMemoryError e)
        {
            System.out.println("OOM error: IN LOADING TO DB function on loop count: " + count);
            e.printStackTrace();
        }

String preEndTime= new SimpleDateFormat("HH:mm:ss").format(Calendar.getInstance().getTime());
        System.out.println("Preprocessing Ended:" + preEndTime);



        //Insert start time
        String startTime= new SimpleDateFormat("HH:mm:ss").format(Calendar.getInstance().getTime());
        System.out.println("Insert Started:" + startTime);

        // execute the prepared statement as a batch
        long[] results = preparedStmt.executeLargeBatch();

        System.out.println("Update Count size: "+ results.length);

        //Insert end time
        String endTime = new SimpleDateFormat("HH:mm:ss").format(Calendar.getInstance().getTime());
        System.out.println("Insert Completed:" + endTime);

遇到错误:

    OOM error: IN LOADING TO DB function on loop count: 1888076
java.lang.OutOfMemoryError: GC overhead limit exceeded
    at com.mysql.jdbc.SingleByteCharsetConverter.toBytesWrapped(SingleByteCharsetConverter.java:230)
    at com.mysql.jdbc.StringUtils.getBytesWrapped(StringUtils.java:652)
    at com.mysql.jdbc.PreparedStatement.setString(PreparedStatement.java:4005)
    at LoadNTriplesByScript.loadTriplesByBatches(LoadNTriplesByScript.java:282)
    at LoadNTriplesByScript.insertNTriplesToDB(LoadNTriplesByScript.java:137)
    at LoadNTriplesByScript.main(LoadNTriplesByScript.java:77)

Exception in thread "main" java.lang.OutOfMemoryError: GC overhead limit exceeded
    at java.lang.Object.clone(Native Method)
    at java.util.TimeZone.clone(TimeZone.java:738)
    at sun.util.calendar.ZoneInfo.clone(ZoneInfo.java:647)
    at java.util.TimeZone.getDefault(TimeZone.java:625)
    at java.text.SimpleDateFormat.initializeCalendar(SimpleDateFormat.java:657)
    at java.text.SimpleDateFormat.<init>(SimpleDateFormat.java:601)
    at java.text.SimpleDateFormat.<init>(SimpleDateFormat.java:580)
    at LoadNTriplesByScript.loadTriplesByBatches(LoadNTriplesByScript.java:330)
    at LoadNTriplesByScript.insertNTriplesToDB(LoadNTriplesByScript.java:137)
    at LoadNTriplesByScript.main(LoadNTriplesByScript.java:77)

Eclipse版本:Oxygen.1a版本(4.7.1a)

Java堆大小:-Xms2048m -Xmx3072m

我不确定这是否是由字符串操作(split()方法)引起的,或者是否在PreparedStatement上有一个限制的addBatch()方法。每批行数。我在执行PreparedStatement后设置了autoCommit(FALSE)然后commit()。

注意:我已经阅读了大量关于GC Overhead和Java Heap Errors的文章广告帖子。关于为什么会发生这种情况的任何建议或指示都表示赞赏。

java mysql garbage-collection heap-memory
1个回答
0
投票

很有可能,你只是内存不足。该消息具有误导性,因为高GC开销通常很简单,因为没有任何东西要收集(所有内存都在使用中)。

批处理的原因是避免高命令开销。关于性能,如果您使用1000或1000000行的批次,这几乎不重要。所以尝试小批量。

如果您的应用程序可以暂时看到不完整的数据,也可以尝试较小的提交。

请注意,你要记住triples中的原始行和tripleToInsert中的split行。对于1.8M行和3 GB总计,每行可以使用少于约1500字节。考虑到一个字符在Java中是两个字节,考虑到你将它存储两次并且总是有一些开销,所以它可以用于每行<300个字符(只是一个猜测)。

你基本上做得对,只是这么大的批量需要大量的内存。如果这只是一个导入,那么你可以通过清除未分割的三元组来节省一些内存。

有时可以使用DB提供的内置CSV导入。

© www.soinside.com 2019 - 2024. All rights reserved.