L'optimisation de SQLite est délicate. Les performances d'insertion en vrac d'une application C peuvent varier de 85 inserts par seconde à plus de 96 000 inserts par seconde!
Contexte: Nous utilisons SQLite dans le cadre d'une application de bureau. Nous avons de grandes quantités de données de configuration stockées dans des fichiers XML qui sont analysées et chargées dans une base de données SQLite pour un traitement ultérieur lorsque l'application est initialisée. SQLite est idéal pour cette situation car il est rapide, il ne nécessite aucune configuration spécialisée et la base de données est stockée sur disque en tant que fichier unique.
Justification: Au début, j'étais déçu de la performance que je voyais. Il s'avère que les performances de SQLite peuvent varier considérablement (à la fois pour les insertions en masse et les sélections) en fonction de la configuration de la base de données et de la façon dont vous utilisez l'API. Ce n'était pas une question triviale de comprendre quelles étaient toutes les options et techniques, donc j'ai pensé qu'il était prudent de créer cette entrée wiki communautaire pour partager les résultats avec les lecteurs Stack Overflow afin de sauver les autres des ennuis des mêmes investigations.
L'expérience: Plutôt que de simplement parler de conseils de performance au sens général (c'est-à-dire "Utiliser une transaction!" ), J'ai pensé qu'il était préférable d'écrire du code C et de mesurer réellement l'impact de diverses options. Nous allons commencer avec quelques données simples:
- Un fichier texte délimité par une tabulation de 28 Mo (environ 865 000 enregistrements) de l' horaire de transport en commun complet de la ville de Toronto
- Ma machine de test est un P4 3,60 GHz fonctionnant sous Windows XP.
- Le code est compilé avec Visual C ++ 2005 comme "Release" avec "Full Optimization" (/ Ox) et Favoriser Fast Code (/ Ot).
- J'utilise SQLite "Amalgamation", compilé directement dans mon application de test. La version de SQLite dont je dispose est un peu plus ancienne (3.6.7), mais je pense que ces résultats seront comparables à la dernière version (veuillez laisser un commentaire si vous pensez le contraire).
Écrivons du code!
Le code: un programme C simple qui lit le fichier texte ligne par ligne, divise la chaîne en valeurs, puis insère les données dans une base de données SQLite. Dans cette version "baseline" du code, la base de données est créée, mais nous n'insérerons pas réellement de données:
/*************************************************************
Baseline code to experiment with SQLite performance.
Input data is a 28 MB TAB-delimited text file of the
complete Toronto Transit System schedule/route info
from http://www.toronto.ca/open/datasets/ttc-routes/
**************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
#include "sqlite3.h"
#define INPUTDATA "C:\\TTC_schedule_scheduleitem_10-27-2009.txt"
#define DATABASE "c:\\TTC_schedule_scheduleitem_10-27-2009.sqlite"
#define TABLE "CREATE TABLE IF NOT EXISTS TTC (id INTEGER PRIMARY KEY, Route_ID TEXT, Branch_Code TEXT, Version INTEGER, Stop INTEGER, Vehicle_Index INTEGER, Day Integer, Time TEXT)"
#define BUFFER_SIZE 256
int main(int argc, char **argv) {
sqlite3 * db;
sqlite3_stmt * stmt;
char * sErrMsg = 0;
char * tail = 0;
int nRetCode;
int n = 0;
clock_t cStartClock;
FILE * pFile;
char sInputBuf [BUFFER_SIZE] = "\0";
char * sRT = 0; /* Route */
char * sBR = 0; /* Branch */
char * sVR = 0; /* Version */
char * sST = 0; /* Stop Number */
char * sVI = 0; /* Vehicle */
char * sDT = 0; /* Date */
char * sTM = 0; /* Time */
char sSQL [BUFFER_SIZE] = "\0";
/*********************************************/
/* Open the Database and create the Schema */
sqlite3_open(DATABASE, &db);
sqlite3_exec(db, TABLE, NULL, NULL, &sErrMsg);
/*********************************************/
/* Open input file and import into Database*/
cStartClock = clock();
pFile = fopen (INPUTDATA,"r");
while (!feof(pFile)) {
fgets (sInputBuf, BUFFER_SIZE, pFile);
sRT = strtok (sInputBuf, "\t"); /* Get Route */
sBR = strtok (NULL, "\t"); /* Get Branch */
sVR = strtok (NULL, "\t"); /* Get Version */
sST = strtok (NULL, "\t"); /* Get Stop Number */
sVI = strtok (NULL, "\t"); /* Get Vehicle */
sDT = strtok (NULL, "\t"); /* Get Date */
sTM = strtok (NULL, "\t"); /* Get Time */
/* ACTUAL INSERT WILL GO HERE */
n++;
}
fclose (pFile);
printf("Imported %d records in %4.2f seconds\n", n, (clock() - cStartClock) / (double)CLOCKS_PER_SEC);
sqlite3_close(db);
return 0;
}
Le contrôle"
L'exécution du code en l'état n'effectue en fait aucune opération de base de données, mais il nous donnera une idée de la vitesse des opérations d'E / S du fichier C brut et du traitement des chaînes.
Importé 864913 enregistrements en 0,94 secondes
Génial! Nous pouvons faire 920 000 insertions par seconde, à condition que nous n'effectuions aucune insertion :-)
Le "pire scénario"
Nous allons générer la chaîne SQL en utilisant les valeurs lues dans le fichier et invoquer cette opération SQL en utilisant sqlite3_exec:
sprintf(sSQL, "INSERT INTO TTC VALUES (NULL, '%s', '%s', '%s', '%s', '%s', '%s', '%s')", sRT, sBR, sVR, sST, sVI, sDT, sTM);
sqlite3_exec(db, sSQL, NULL, NULL, &sErrMsg);
Cela va être lent car le SQL sera compilé en code VDBE pour chaque insertion et chaque insertion se produira dans sa propre transaction. C'est lent?
Importé 864913 enregistrements en 9933,61 secondes
Oui! 2 heures et 45 minutes! Cela ne représente que 85 insertions par seconde.
Utilisation d'une transaction
Par défaut, SQLite évaluera chaque instruction INSERT / UPDATE dans une transaction unique. Si vous effectuez un grand nombre d'insertions, il est conseillé d'envelopper votre opération dans une transaction:
sqlite3_exec(db, "BEGIN TRANSACTION", NULL, NULL, &sErrMsg);
pFile = fopen (INPUTDATA,"r");
while (!feof(pFile)) {
...
}
fclose (pFile);
sqlite3_exec(db, "END TRANSACTION", NULL, NULL, &sErrMsg);
Importé 864913 enregistrements en 38,03 secondes
C'est mieux. Le simple emballage de tous nos inserts en une seule transaction a amélioré nos performances à 23 000 inserts par seconde.
Utilisation d'une instruction préparée
L'utilisation d'une transaction a été une énorme amélioration, mais recompiler l'instruction SQL pour chaque insert n'a pas de sens si nous utilisons le même SQL à plusieurs reprises. Utilisons sqlite3_prepare_v2
pour compiler notre instruction SQL une fois, puis lions nos paramètres à cette instruction en utilisant sqlite3_bind_text
:
/* Open input file and import into the database */
cStartClock = clock();
sprintf(sSQL, "INSERT INTO TTC VALUES (NULL, @RT, @BR, @VR, @ST, @VI, @DT, @TM)");
sqlite3_prepare_v2(db, sSQL, BUFFER_SIZE, &stmt, &tail);
sqlite3_exec(db, "BEGIN TRANSACTION", NULL, NULL, &sErrMsg);
pFile = fopen (INPUTDATA,"r");
while (!feof(pFile)) {
fgets (sInputBuf, BUFFER_SIZE, pFile);
sRT = strtok (sInputBuf, "\t"); /* Get Route */
sBR = strtok (NULL, "\t"); /* Get Branch */
sVR = strtok (NULL, "\t"); /* Get Version */
sST = strtok (NULL, "\t"); /* Get Stop Number */
sVI = strtok (NULL, "\t"); /* Get Vehicle */
sDT = strtok (NULL, "\t"); /* Get Date */
sTM = strtok (NULL, "\t"); /* Get Time */
sqlite3_bind_text(stmt, 1, sRT, -1, SQLITE_TRANSIENT);
sqlite3_bind_text(stmt, 2, sBR, -1, SQLITE_TRANSIENT);
sqlite3_bind_text(stmt, 3, sVR, -1, SQLITE_TRANSIENT);
sqlite3_bind_text(stmt, 4, sST, -1, SQLITE_TRANSIENT);
sqlite3_bind_text(stmt, 5, sVI, -1, SQLITE_TRANSIENT);
sqlite3_bind_text(stmt, 6, sDT, -1, SQLITE_TRANSIENT);
sqlite3_bind_text(stmt, 7, sTM, -1, SQLITE_TRANSIENT);
sqlite3_step(stmt);
sqlite3_clear_bindings(stmt);
sqlite3_reset(stmt);
n++;
}
fclose (pFile);
sqlite3_exec(db, "END TRANSACTION", NULL, NULL, &sErrMsg);
printf("Imported %d records in %4.2f seconds\n", n, (clock() - cStartClock) / (double)CLOCKS_PER_SEC);
sqlite3_finalize(stmt);
sqlite3_close(db);
return 0;
Importé 864913 enregistrements en 16,27 secondes
Agréable! Il y a un peu plus de code (n'oubliez pas d'appeler sqlite3_clear_bindings
et sqlite3_reset
), mais nous avons plus que doublé nos performances à 53 000 insertions par seconde.
PRAGMA synchrone = OFF
Par défaut, SQLite se met en pause après l'émission d'une commande d'écriture au niveau du système d'exploitation. Cela garantit que les données sont écrites sur le disque. En définissant synchronous = OFF
, nous demandons à SQLite de simplement transmettre les données au système d'exploitation pour l'écriture, puis de continuer. Il est possible que le fichier de base de données soit corrompu si l'ordinateur subit un crash catastrophique (ou une panne de courant) avant que les données ne soient écrites sur le plateau:
/* Open the database and create the schema */
sqlite3_open(DATABASE, &db);
sqlite3_exec(db, TABLE, NULL, NULL, &sErrMsg);
sqlite3_exec(db, "PRAGMA synchronous = OFF", NULL, NULL, &sErrMsg);
Importé 864913 enregistrements en 12,41 secondes
Les améliorations sont maintenant plus petites, mais nous atteignons 69 600 inserts par seconde.
PRAGMA journal_mode = MEMOIRE
Envisagez de stocker le journal de restauration en mémoire en procédant à une évaluation PRAGMA journal_mode = MEMORY
. Votre transaction sera plus rapide, mais si vous perdez de l'énergie ou que votre programme tombe en panne pendant une transaction, votre base de données peut être laissée dans un état corrompu avec une transaction partiellement terminée:
/* Open the database and create the schema */
sqlite3_open(DATABASE, &db);
sqlite3_exec(db, TABLE, NULL, NULL, &sErrMsg);
sqlite3_exec(db, "PRAGMA journal_mode = MEMORY", NULL, NULL, &sErrMsg);
Importé 864913 enregistrements en 13,50 secondes
Un peu plus lent que l'optimisation précédente à 64 000 insertions par seconde.
PRAGMA synchrone = OFF et PRAGMA journal_mode = MEMORY
Combinons les deux optimisations précédentes. C'est un peu plus risqué (en cas de plantage), mais nous importons simplement des données (pas une banque):
/* Open the database and create the schema */
sqlite3_open(DATABASE, &db);
sqlite3_exec(db, TABLE, NULL, NULL, &sErrMsg);
sqlite3_exec(db, "PRAGMA synchronous = OFF", NULL, NULL, &sErrMsg);
sqlite3_exec(db, "PRAGMA journal_mode = MEMORY", NULL, NULL, &sErrMsg);
Importé 864913 enregistrements en 12,00 secondes
Fantastique! Nous sommes capables de faire 72 000 insertions par seconde.
Utilisation d'une base de données en mémoire
Juste pour les coups de pied, construisons sur toutes les optimisations précédentes et redéfinissons le nom de fichier de la base de données afin que nous travaillions entièrement en RAM:
#define DATABASE ":memory:"
Importé 864913 enregistrements en 10,94 secondes
Ce n'est pas super pratique de stocker notre base de données dans la RAM, mais il est impressionnant de pouvoir effectuer 79 000 insertions par seconde.
Refactorisation du code C
Bien qu'il ne s'agisse pas spécifiquement d'une amélioration SQLite, je n'aime pas les char*
opérations d'affectation supplémentaires dans la while
boucle. Refactorisons rapidement ce code pour passer strtok()
directement la sortie de sqlite3_bind_text()
, et laissons le compilateur essayer d'accélérer les choses pour nous:
pFile = fopen (INPUTDATA,"r");
while (!feof(pFile)) {
fgets (sInputBuf, BUFFER_SIZE, pFile);
sqlite3_bind_text(stmt, 1, strtok (sInputBuf, "\t"), -1, SQLITE_TRANSIENT); /* Get Route */
sqlite3_bind_text(stmt, 2, strtok (NULL, "\t"), -1, SQLITE_TRANSIENT); /* Get Branch */
sqlite3_bind_text(stmt, 3, strtok (NULL, "\t"), -1, SQLITE_TRANSIENT); /* Get Version */
sqlite3_bind_text(stmt, 4, strtok (NULL, "\t"), -1, SQLITE_TRANSIENT); /* Get Stop Number */
sqlite3_bind_text(stmt, 5, strtok (NULL, "\t"), -1, SQLITE_TRANSIENT); /* Get Vehicle */
sqlite3_bind_text(stmt, 6, strtok (NULL, "\t"), -1, SQLITE_TRANSIENT); /* Get Date */
sqlite3_bind_text(stmt, 7, strtok (NULL, "\t"), -1, SQLITE_TRANSIENT); /* Get Time */
sqlite3_step(stmt); /* Execute the SQL Statement */
sqlite3_clear_bindings(stmt); /* Clear bindings */
sqlite3_reset(stmt); /* Reset VDBE */
n++;
}
fclose (pFile);
Remarque: Nous revenons à l'utilisation d'un vrai fichier de base de données. Les bases de données en mémoire sont rapides, mais pas nécessairement pratiques
Importé 864913 enregistrements en 8,94 secondes
Une légère refactorisation du code de traitement de chaîne utilisé dans notre liaison de paramètres nous a permis d'effectuer 96 700 insertions par seconde. Je pense qu'il est prudent de dire que c'est très rapide . Alors que nous commençons à modifier d'autres variables (par exemple, la taille de la page, la création d'index, etc.), ce sera notre référence.
Résumé (jusqu'à présent)
J'espère que tu es toujours avec moi! La raison pour laquelle nous avons commencé dans cette voie est que les performances de l'insertion en vrac varient énormément avec SQLite, et il n'est pas toujours évident de savoir quelles modifications doivent être apportées pour accélérer nos opérations. En utilisant le même compilateur (et les options du compilateur), la même version de SQLite et les mêmes données, nous avons optimisé notre code et notre utilisation de SQLite pour passer du pire des cas de 85 insertions par seconde à plus de 96 000 insertions par seconde!
CRÉER INDEX puis INSÉRER vs INSÉRER puis CRÉER INDEX
Avant de commencer à mesurer les SELECT
performances, nous savons que nous allons créer des indices. Il a été suggéré dans l'une des réponses ci-dessous que lors de l'insertion en bloc, il est plus rapide de créer l'index après l'insertion des données (par opposition à la création de l'index en premier, puis à l'insertion des données). Essayons:
Créer un index puis insérer des données
sqlite3_exec(db, "CREATE INDEX 'TTC_Stop_Index' ON 'TTC' ('Stop')", NULL, NULL, &sErrMsg);
sqlite3_exec(db, "BEGIN TRANSACTION", NULL, NULL, &sErrMsg);
...
864913 enregistrements importés en 18.13 secondes
Insérer des données puis créer un index
...
sqlite3_exec(db, "END TRANSACTION", NULL, NULL, &sErrMsg);
sqlite3_exec(db, "CREATE INDEX 'TTC_Stop_Index' ON 'TTC' ('Stop')", NULL, NULL, &sErrMsg);
Importé 864913 enregistrements en 13,66 secondes
Comme prévu, les insertions groupées sont plus lentes si une colonne est indexée, mais cela fait une différence si l'index est créé après l'insertion des données. Notre base de référence sans indice est de 96 000 insertions par seconde. La création de l'index en premier, puis l'insertion des données nous donne 47 700 insertions par seconde, tandis que l'insertion des données en premier puis la création de l'index nous donne 63 300 insertions par seconde.
Je serais ravi de prendre des suggestions pour d'autres scénarios à essayer ... Et je vais bientôt compiler des données similaires pour les requêtes SELECT.
la source
sqlite3_clear_bindings(stmt);
? Vous définissez les liaisons à chaque fois, ce qui devrait être suffisant: Avant d'appeler sqlite3_step () pour la première fois ou immédiatement après sqlite3_reset (), l'application peut appeler l'une des interfaces sqlite3_bind () pour attacher des valeurs aux paramètres. Chaque appel à sqlite3_bind () remplace les liaisons précédentes sur le même paramètre (voir: sqlite.org/cintro.html ). Il n'y a rien dans les documents pour cette fonction qui dit que vous devez l'appeler.feof()
pour contrôler la terminaison de votre boucle d'entrée. Utilisez le résultat renvoyé parfgets()
. stackoverflow.com/a/15485689/827263Réponses:
Quelques conseils:
pragma journal_mode
). Il y aNORMAL
, et puis il y aOFF
, ce qui peut augmenter considérablement la vitesse d'insertion si vous n'êtes pas trop inquiet de la possibilité que la base de données soit corrompue si le système d'exploitation se bloque. Si votre application plante, les données devraient être correctes. Notez que dans les versions plus récentes, lesOFF/MEMORY
paramètres ne sont pas sûrs pour les plantages au niveau de l'application.PRAGMA page_size
). Avoir des tailles de page plus grandes peut accélérer la lecture et l'écriture car les pages plus grandes sont conservées en mémoire. Notez que davantage de mémoire sera utilisée pour votre base de données.CREATE INDEX
après avoir fait toutes vos insertions. C'est beaucoup plus rapide que de créer l'index puis de faire vos insertions.INTEGER PRIMARY KEY
si possible, ce qui remplacera la colonne de numéro de ligne unique implicite dans le tableau.!feof(file)
!J'ai également posé des questions similaires ici et ici .
la source
Essayez d'utiliser
SQLITE_STATIC
au lieu deSQLITE_TRANSIENT
pour ces insertions.SQLITE_TRANSIENT
entraînera SQLite pour copier les données de chaîne avant de retourner.SQLITE_STATIC
lui indique que l'adresse mémoire que vous lui avez donnée sera valide jusqu'à ce que la requête soit effectuée (ce qui est toujours le cas dans cette boucle). Cela vous évitera plusieurs opérations d'allocation, de copie et de désallocation par boucle. Peut-être une grande amélioration.la source
A éviter
sqlite3_clear_bindings(stmt)
.Le code du test définit à chaque fois les liaisons qui devraient suffire.
L' intro de l' API C des documents SQLite dit:
Il n'y a rien dans les documents pour
sqlite3_clear_bindings
dire que vous devez l'appeler en plus de simplement définir les liaisons.Plus de détails: Avoid_sqlite3_clear_bindings ()
la source
Sur inserts en vrac
Inspiré par ce post et par la question de débordement de pile qui m'a conduit ici - Est-il possible d'insérer plusieurs lignes à la fois dans une base de données SQLite? - J'ai posté mon premier dépôt Git :
https://github.com/rdpoor/CreateOrUpdatequi charge en masse un tableau d'ActiveRecords dans les bases de données MySQL , SQLite ou PostgreSQL . Il comprend une option pour ignorer les enregistrements existants, les écraser ou déclencher une erreur. Mes repères rudimentaires montrent une amélioration de la vitesse 10x par rapport aux écritures séquentielles - YMMV.
Je l'utilise dans le code de production où j'ai souvent besoin d'importer de grands ensembles de données, et j'en suis assez content.
la source
Les importations en masse semblent fonctionner mieux si vous pouvez fragmenter vos instructions INSERT / UPDATE . Une valeur de 10 000 environ a bien fonctionné pour moi sur une table avec seulement quelques lignes, YMMV ...
la source
Si vous ne vous souciez que de la lecture, la version un peu plus rapide (mais peut lire les données périmées) consiste à lire à partir de plusieurs connexions à partir de plusieurs threads (connexion par thread).
Trouvez d'abord les articles, dans le tableau:
puis lisez en pages (LIMIT / OFFSET):
où et sont calculés par thread, comme ceci:
pour chaque fil:
Pour notre petite base de données (200 Mo), cela a accéléré de 50 à 75% (3.8.0.2 64 bits sur Windows 7). Nos tableaux sont fortement non normalisés (1 000 à 1 500 colonnes, environ 100 000 lignes ou plus).
Trop ou trop peu de fils ne le feront pas, vous devez vous évaluer et vous profiler.
Aussi pour nous, SHAREDCACHE a ralenti les performances, j'ai donc mis manuellement PRIVATECACHE (car il a été activé globalement pour nous)
la source
Je ne pourrais obtenir aucun gain des transactions jusqu'à ce que j'augmente cache_size à une valeur plus élevée, c.-à-d.
PRAGMA cache_size=10000;
la source
cache_size
définit le nombre de pages à mettre en cache et non la taille totale de la RAM. Avec la taille de page par défaut de 4 Ko, ce paramètre contiendra jusqu'à 40 Mo de données par fichier ouvert (ou par processus, s'il s'exécute avec un cache partagé ).Après avoir lu ce tutoriel, j'ai essayé de l'implémenter dans mon programme.
J'ai 4-5 fichiers qui contiennent des adresses. Chaque fichier contient environ 30 millions d'enregistrements. J'utilise la même configuration que celle que vous proposez, mais mon nombre d'insertions par seconde est très faible (~ 10 000 enregistrements par seconde).
Voici où votre suggestion échoue. Vous utilisez une seule transaction pour tous les enregistrements et une seule insertion sans erreur / échec. Disons que vous divisez chaque enregistrement en plusieurs insertions sur différentes tables. Que se passe-t-il si le record est battu?
La commande ON CONFLICT ne s'applique pas, car si vous avez 10 éléments dans un enregistrement et que vous avez besoin que chaque élément soit inséré dans une table différente, si l'élément 5 obtient une erreur CONSTRAINT, les 4 insertions précédentes doivent également y aller.
Voici donc où vient le retour en arrière. Le seul problème avec la restauration est que vous perdez toutes vos insertions et commencez par le haut. Comment pouvez-vous résoudre ce problème?
Ma solution était d'utiliser plusieurs transactions. Je commence et termine une transaction tous les 10 000 enregistrements (ne demandez pas pourquoi ce nombre, c'était le plus rapide que j'ai testé). J'ai créé un tableau de 10 000 et j'y ai inséré les enregistrements réussis. Lorsque l'erreur se produit, je fais une restauration, commence une transaction, insère les enregistrements de mon tableau, valide et puis commence une nouvelle transaction après l'enregistrement cassé.
Cette solution m'a aidé à contourner les problèmes que j'ai lorsque je traite des fichiers contenant des enregistrements incorrects / en double (j'avais près de 4% d'enregistrements incorrects).
L'algorithme que j'ai créé m'a aidé à réduire mon processus de 2 heures. Processus de chargement final du fichier 1h30 qui est encore lent mais pas comparé aux 4h qu'il a fallu initialement. J'ai réussi à accélérer les inserts de 10.000 / s à ~ 14.000 / s
Si quelqu'un a d'autres idées sur la façon de l'accélérer, je suis ouvert aux suggestions.
MISE À JOUR :
En plus de ma réponse ci-dessus, vous devez garder à l'esprit que les insertions par seconde en fonction du disque dur que vous utilisez également. Je l'ai testé sur 3 PC différents avec différents disques durs et j'ai eu d'énormes différences dans le temps. PC1 (1 h 30), PC2 (6 h) PC3 (14 h), alors j'ai commencé à me demander pourquoi.
Après deux semaines de recherche et de vérification de plusieurs ressources: Disque dur, RAM, Cache, j'ai découvert que certains paramètres de votre disque dur peuvent affecter le taux d'E / S. En cliquant sur les propriétés de votre lecteur de sortie souhaité, vous pouvez voir deux options dans l'onglet général. Opt1: compresser ce lecteur, Opt2: autoriser l'indexation du contenu des fichiers de ce lecteur.
En désactivant ces deux options, les 3 PC prennent maintenant environ le même temps pour terminer (1 heure et 20 à 40 minutes). Si vous rencontrez des insertions lentes, vérifiez si votre disque dur est configuré avec ces options. Cela vous fera gagner beaucoup de temps et vous évitera des maux de tête en essayant de trouver la solution
la source
La réponse à votre question est que le nouveau SQLite 3 a amélioré les performances, utilisez-le.
Cette réponse Pourquoi SQLAlchemy insert avec sqlite est-il 25 fois plus lent que l'utilisation directe de sqlite3? par SqlAlchemy Orm Author a 100k inserts en 0,5 sec, et j'ai vu des résultats similaires avec python-sqlite et SqlAlchemy. Ce qui m'amène à croire que les performances se sont améliorées avec SQLite 3.
la source
Utilisez ContentProvider pour insérer les données en masse dans db. La méthode ci-dessous utilisée pour insérer des données en masse dans la base de données. Cela devrait améliorer les performances INSERT par seconde de SQLite.
Appelez la méthode bulkInsert:
Lien: https://www.vogella.com/tutorials/AndroidSQLite/article.html consultez Utilisation de la section ContentProvider pour plus de détails
la source