web-dev-qa-db-fra.com

Insertion SQLite très lente?

J'ai récemment lu sur SQLite et j'ai pensé que je pourrais l'essayer. Lorsque j'insère un enregistrement, il fonctionne correctement. Mais lorsque j'en insère cent, cela prend cinq secondes, et à mesure que le nombre d'enregistrements augmente, le temps augmente. Qu'est-ce qui ne va pas? J'utilise le 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();
54
Verve Innovation

Enveloppez les instructions BEGIN\END autour de vos insertions groupées. Sqlite est optimisé pour les transactions.

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();
75
tidwall

Essayez de regrouper toutes vos insertions (aka, une insertion en vrac) dans une seule transaction :

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();
}

Par défaut, SQLite encapsule toutes les insertions dans une transaction , ce qui ralentit le processus:

INSERT est vraiment lent - je ne peux faire que quelques dizaines d'inserts par seconde

En fait, SQLite exécutera facilement 50 000 instructions INSERT ou plus par seconde sur un ordinateur de bureau moyen. Mais il ne fera que quelques dizaines de transactions par seconde.

La vitesse de transaction est limitée par la vitesse du lecteur de disque car (par défaut) SQLite attend réellement que les données soient réellement stockées en toute sécurité sur la surface du disque avant la fin de la transaction. De cette façon, si vous perdez soudainement de l'énergie ou si votre système d'exploitation tombe en panne, vos données sont toujours en sécurité. Pour plus de détails, lisez sur la validation atomique dans SQLite.

Par défaut, chaque instruction INSERT est sa propre transaction. Mais si vous entourez plusieurs instructions INSERT avec BEGIN ... COMMIT, toutes les insertions sont regroupées en une seule transaction. Le temps nécessaire pour valider la transaction est amorti sur toutes les instructions d'insertion jointes et donc le temps par instruction d'insertion est considérablement réduit.

31
Jared Harley

J'ai lu partout que la création de transactions est la solution pour ralentir les écritures SQLite, mais il peut être long et pénible de réécrire votre code et d'envelopper toutes vos écritures SQLite dans des transactions.

J'ai trouvé une méthode beaucoup plus simple, sûre et très efficace: j'active une optimisation SQLite 3.7.0 (désactivée par défaut): le Write-Ahead-Log (WAL) . La documentation indique que cela fonctionne dans tous les systèmes Unix (c'est-à-dire Linux et OSX) et Windows.

Comment ? Exécutez simplement les commandes suivantes après avoir initialisé votre connexion SQLite:

PRAGMA journal_mode = WAL
PRAGMA synchronous = NORMAL

Mon code s'exécute désormais ~ 600% plus rapidement: ma suite de tests s'exécute désormais en 38 secondes au lieu de 4 minutes :)

30
david_p

Voir "Optimisation des requêtes SQL" dans le fichier d'aide ADO.NET SQLite.NET.chm. Code de cette page:

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();
}
8
Scott