JDBC Batch Insert OutOfMemoryError
Я написал метод insert()
, в котором я пытаюсь использовать пакет JDBC для вставки полмиллиона записей в базу данных MySQL:
public void insert(int nameListId, String[] names) {
String sql = "INSERT INTO name_list_subscribers (name_list_id, name, date_added)"+
" VALUES (?, ?, NOW())";
Connection conn = null;
PreparedStatement ps = null;
try{
conn = getConnection();
ps = conn.prepareStatement(sql);
for(String s : names ){
ps.setInt(1, nameListId);
ps.setString(2, s);
ps.addBatch();
}
ps.executeBatch();
}catch(SQLException e){
throw new RuntimeException(e);
}finally{
closeDbResources(ps, null, conn);
}
}
Но всякий раз, когда я пытаюсь запустить этот метод, я получаю следующую ошибку:
java.lang.OutOfMemoryError: Java heap space
com.mysql.jdbc.ServerPreparedStatement$BatchedBindValues.<init>(ServerPreparedStatement.java:72)
com.mysql.jdbc.ServerPreparedStatement.addBatch(ServerPreparedStatement.java:330)
org.apache.commons.dbcp.DelegatingPreparedStatement.addBatch(DelegatingPreparedStatement.java:171)
Если я заменил ps.addBatch()
на ps.executeUpdate()
и удалил ps.executeBatch()
, он отлично работает, хотя требуется некоторое время. Пожалуйста, дайте мне знать, знаете ли вы, подходит ли использование Batch в этой ситуации, и если да, то почему он дает OurOfMemoryError
?
Спасибо
Ответы
Ответ 1
addBatch
и executeBatch
предоставляют вам механизм для выполнения пакетных вставок, но вам все равно необходимо выполнить алгоритм пакетной обработки.
Если вы просто складываете каждый оператор в ту же самую партию, что и вы делаете, тогда у вас закончится нехватка памяти. Вам нужно выполнить/очистить пакет каждые записи n
. Значение n
зависит от вас, JDBC не может принять это решение за вас. Чем больше размер партии, тем быстрее все будет идти, но слишком велико, и вы получите голод на память, и что-то замедлит работу или потерпит неудачу. Это зависит от того, сколько у вас памяти.
Начните с размера партии 1000, например, и поэкспериментируйте с различными значениями оттуда.
final int batchSize = 1000;
int count = 0;
for(String s : names ) {
ps.setInt(1, nameListId);
ps.setString(2, s);
ps.addBatch();
if (++count % batchSize == 0) {
ps.executeBatch();
ps.clearBatch(); //not sure if this is necessary
}
}
ps.executeBatch(); // flush the last few records.
Ответ 2
Он выведен из памяти, потому что он удерживает всю транзакцию в памяти и отправляет ее только в базу данных при вызове executeBatch
.
Если вам не нужно, чтобы он был атомарным и хотел бы получить лучшую производительность, вы можете сохранить счетчик и вызвать executeBatch
каждое n количество записей.