Недостаточно памяти при вставке пакетной записи через jdbc - PullRequest
0 голосов
/ 01 июня 2018

Я хочу скопировать таблицу (10 миллионов записей) в originDB (sqlite3) в другую базу данных с именем targetDB .
Процесс моего метода: чтение данных из исходной таблицы и генерация ResultSet , затем генерация соответствующей вставки sql для каждой записи и выполнение commit для пакетной вставки при подсчетезаписи достигают 10000.
Код выглядит следующим образом:

public void transfer() throws IOException, SQLException {
    targetDBOperate.setCommit(false);//batch insert
    int count = 0;
    String[] cols = parser(propertyPath);//get fields of data table
    String query = "select * from " + originTable;
    ResultSet rs = originDBOperate.executeQuery(query);//get origin table
    String base = "insert into " + targetTable;
    while(rs.next()) {
        count++;
        String insertSql = buildInsertSql(base,rs,cols);//corresponding insert sql
        targetDBOperate.executeSql(insertSql);
        if(count%10000==0) {
            targetDBOperate.commit();// batch insert
        }
    }
    targetDBOperate.closeConnection();
}

На следующем рисунке показана тенденция использования памяти, а вертикальная ось представляет использование памяти enter image description here

Как мы можем сказать, он будет все больше и больше до тех пор, пока не будет заполнен .У переполнения стека возникает вопрос, такой как Недостаточно памяти при вставке записей в SQLite, FireDac, Delphi , но я не могу решить мою проблему, поскольку мы используем другой метод реализации.
Моя гипотеза состоит в том, что когда счетчикзаписи не достигает 10000, эти соответствующие вставки sql будут кэшироваться в памяти, и они не были удалены при выполнении commit по умолчанию?Каждый совет будет оценен.

Ответы [ 2 ]

0 голосов
/ 03 июня 2018

Перемещая большее число строк в SQLite или любой другой реляционной базе данных, вы должны следовать некоторым основным принципам:

1) установить autoCommit в false т.е. не фиксируйте каждую вставку

2) используйте пакетное обновление , т. е. не выполняйте обратную передачу для каждой строки

3) используйте подготовленный оператор т.е. не разбирайте каждую вставку.

Соединяя это вместе, вы получаете следующий код:

cn - исходное соединение, cn2 - целевое соединение.

Для каждой вставленной строки вы звоните addBatch, но только один раз за batchSize вы звоните executeBatch, что инициирует обратную передачу.

Не забудьте последний executeBatch в конце цикла иfinal commit.

cn2.setAutoCommit(false)

String SEL_STMT = "select id, col1,col2 from tab1"
String INS_STMT = "insert into tab2(id, col1,col2) values(?,?,?)"

def batchSize = 10000


def stmt = cn.prepareStatement(SEL_STMT)
def stmtIns = cn2.prepareStatement(INS_STMT) 

rs = stmt.executeQuery()

while(rs.next())
  {
    stmtIns.setLong(1,rs.getLong(1))
    stmtIns.setString(2,rs.getString(2))
    stmtIns.setTimestamp(3,rs.getTimestamp(3))
    stmtIns.addBatch();
    i += 1

    if (i == batchSize) {
        def insRec = stmtIns.executeBatch();
        i = 0
        }

  }
rs.close()
stmt.close()

def insRec = stmtIns.executeBatch();

stmtIns.close()
cn2.commit()

Пример теста с вашим размером с sqlite-jdbc-3.23.1:

inserted rows: 10000000
total time taken to insert the batch = 46848 ms

Я не наблюдаю никаких проблем с памятью или проблем с большой транзакцией

0 голосов
/ 01 июня 2018

Вы пытаетесь получить 10M записей за один раз, выполнив следующие действия.Это определенно съест вашу память как что-либо

String query = "select * from " + originTable;
ResultSet rs = originDBOperate.executeQuery(query);//get origin table

Используйте разбитые на страницы запросы для чтения пакетов и выполнения пакетных обновлений согласно.

Вы даже не выполняете пакетное обновление Вы просто запускаете 10Kзапрашивает один за другим, выполнив следующий код

String insertSql = buildInsertSql(base,rs,cols);//corresponding insert sql
    targetDBOperate.executeSql(insertSql);
    if(count%10000==0) {
        targetDBOperate.commit();// This simply means that you are commiting after 10K records
    }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...