Я пытаюсь вставить около 1,9 миллиона строк сразу в виде пакетной вставки в MySQL. Код работает как шарм с 1,7 мили или меньше строк, но я получаю ошибки накладных расходов и/или иногда ошибки Java Heap Space для строк более 1,8 мили.
Код:
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 Release (4.7.1a)
Размеры кучи Java: -Xms2048m -Xmx3072m
Я не уверен, вызвано ли это методом манипуляции String (split()), или если существует метод LIMIT в методах AddBatch() PreparedStatement на no. из рядов на партию. Я установил autoCommit (FALSE), а затем commit() после выполнения PreparedStatement.
Примечание. Я прочитал множество статей, посвященных сообщениям об ошибках в области ОХ и ошибок Java. Приветствуется любое предложение или указатель относительно того, почему это происходит.
С очень высокой вероятностью у вас просто закончилась память. Сообщение вводит в заблуждение, так как высокая накладная стоимость GC обычно прост, вызванный отсутствием сбора (вся память используется).
Причиной для пакетной обработки является исключение высоких затрат на одну команду. Что касается производительности, вряд ли имеет значение, если вы используете партии из 1000 или 1000000 строк. Поэтому попробуйте меньшие партии.
Возможно, также попробуйте выполнить мелкие коммиты, если ваше приложение может жить с просмотром неполных данных некоторое время.
Обратите внимание, что вы сохраняете в памяти как исходную строку в triples
и разделительную в tripleToInsert
. С 1,8 метрами и общим объемом 3 ГБ вы можете использовать менее 1500 байт за строку. Учтите, что в Java есть два байта, считайте, что вы храните его дважды, и что всегда есть накладные расходы, поэтому он может работать, возможно, <300 символов в строке (просто догадка).
Вы в основном делаете это правильно, просто для такой огромной партии требуется много памяти. Если это просто импорт, вы можете сэкономить некоторое количество памяти, очистив нерасщепленные тройки, когда они будут потребляться.
Иногда можно использовать встроенный CSV-импорт, предоставляемый БД.
x
, с одной огромной партией -x/1.8e6
, с партиями размера 1000, этоx/1000
. Так что это то, что я попробую первым.+++
Экспорт в CSV немного уродлив, но может иметь смысл и может быть самым простым решением. Преимущество заключается в том, чтобы держать вашу Java занятой в течение кратчайшего времени.+++
Оптимизация потребления памяти может помочь, но когда ваши данные сильно растут, вы снова столкнетесь с той же проблемой.