SQLite Вставить очень медленно? - PullRequest
54 голосов
/ 04 октября 2010

Я недавно читал о SQLite и думал, что попробую. Когда я вставляю одну запись, она работает нормально. Но когда я вставляю сотню, это занимает пять секунд, а с увеличением количества записей увеличивается и время. Что может быть не так? Я использую SQLite Wrapper (system.data.SQlite):

dbcon = new SQLiteConnection(connectionString);
dbcon.Open();

//---INSIDE LOOP

 SQLiteCommand sqlComm = new SQLiteCommand(sqlQuery, dbcon);

 nRowUpdatedCount = sqlComm.ExecuteNonQuery(); 

//---END LOOP

dbcon.close();

Ответы [ 4 ]

75 голосов
/ 04 октября 2010

Оберните BEGIN \ END операторы вокруг ваших массовых вставок.Sqlite оптимизирован для транзакций.

dbcon = new SQLiteConnection(connectionString);
dbcon.Open();

SQLiteCommand sqlComm;
sqlComm = new SQLiteCommand("begin", dbcon);
sqlComm.ExecuteNonQuery(); 
//---INSIDE LOOP

 sqlComm = new SQLiteCommand(sqlQuery, dbcon);

 nRowUpdatedCount = sqlComm.ExecuteNonQuery(); 

//---END LOOP
sqlComm = new SQLiteCommand("end", dbcon);
sqlComm.ExecuteNonQuery(); 
dbcon.close();
31 голосов
/ 04 октября 2010

Попробуйте обернуть все ваши вставки (иначе говоря, массовая вставка) в одну транзакцию :

string insertString = "INSERT INTO [TableName] ([ColumnName]) Values (@value)";

SQLiteCommand command = new SQLiteCommand();
command.Parameters.AddWithValue("@value", value);
command.CommandText = insertString;
command.Connection = dbConnection;
SQLiteTransaction transaction = dbConnection.BeginTransaction();
try
{
    //---INSIDE LOOP
    SQLiteCommand sqlComm = new SQLiteCommand(sqlQuery, dbcon);
    nRowUpdatedCount = sqlComm.ExecuteNonQuery(); 
    //---END LOOP

    transaction.Commit();
    return true;
}
catch (SQLiteException ex)
{
    transaction.Rollback();
}

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

INSERT действительно медленный - я могу сделать только несколько десятков INSERT в секунду

На самом деле, SQLite легко сделает50 000 или более операторов INSERT в секунду на обычном настольном компьютере.Но он будет выполнять всего несколько десятков транзакций в секунду.

Скорость транзакции ограничена скоростью диска, потому что (по умолчанию) SQLite фактически ожидает, пока данные действительно не будут безопасно сохранены на поверхности диска, прежде чем транзакция будет завершена,Таким образом, если вы вдруг потеряете питание или ваша ОС выйдет из строя, ваши данные останутся в безопасности.Подробнее читайте об атомарном коммите в SQLite.

По умолчанию каждый оператор INSERT является собственной транзакцией.Но если вы окружите несколько операторов INSERT с помощью BEGIN ... COMMIT, тогда все вставки будут сгруппированы в одну транзакцию.Время, необходимое для фиксации транзакции, амортизируется по всем вложенным операторам вставки, поэтому время на оператор вставки значительно сокращается.

30 голосов
/ 04 декабря 2014

Я везде читал, что создание транзакций - это решение для медленной записи SQLite, но переписать ваш код и обернуть все записи SQLite в транзакции может быть долгим и мучительным.

Я нашел гораздо более простой, безопасныйи очень эффективный метод: я включаю (по умолчанию отключено) оптимизацию SQLite 3.7.0: Write-Ahead-Log (WAL) .В документации говорится, что она работает во всех системах Unix (т.е. Linux и OSX) и Windows.

Как?Просто выполните следующие команды после инициализации соединения SQLite:

PRAGMA journal_mode = WAL
PRAGMA synchronous = NORMAL

Мой код теперь работает на ~ 600% быстрее: мой набор тестов теперь выполняется за 38 секунд вместо 4 минут:)

8 голосов
/ 21 января 2011

См. «Оптимизация запросов SQL» в файле справки ADO.NET SQLite.NET.chm. Код с этой страницы:

using (SQLiteTransaction mytransaction = myconnection.BeginTransaction())
{
  using (SQLiteCommand mycommand = new SQLiteCommand(myconnection))
  {
    SQLiteParameter myparam = new SQLiteParameter();
    int n;

    mycommand.CommandText = "INSERT INTO [MyTable] ([MyId]) VALUES(?)";
    mycommand.Parameters.Add(myparam);

    for (n = 0; n < 100000; n ++)
    {
      myparam.Value = n + 1;
      mycommand.ExecuteNonQuery();
    }
  }
  mytransaction.Commit();
}
...