Améliorer la performance INSERT-per-second de SQLite?

optimiser SQLite est délicat. La performance d'une application C peut varier de 85 inserts par seconde à plus de 96 000 inserts par seconde!

Contexte: Nous sommes en utilisant 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és et chargés 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 parce qu'il est rapide, il ne nécessite aucune configuration spécialisée, et la base de données est stockée sur le disque comme un seul fichier.

justification: au départ, j'ai été déçu de la performance que je voyais. il s'avère que les performances de SQLite peuvent varier de manière significative (à la fois pour les inserts en vrac et les sélections) en fonction de la façon dont la base de données est configurée et comment vous utilisez l'API. Ce n'était pas une question insignifiante de comprendre ce que toutes les options et techniques étaient, donc j'ai pensé qu'il était prudent de créer cette entrée wiki communautaire pour partager les résultats avec les lecteurs de débordement de la pile afin d'épargner aux autres les problèmes des mêmes enquêtes.

L'expérience: plutôt que de simplement parler de conseils de performance dans le sens général (i.e. " utilisez une transaction!" ), j'ai pensé qu'il était préférable d'écrire un code C et mesurer réellement l'impact de différentes options. Nous allons commencer avec quelques données simples:

  • UN 28 MO fichier texte délimité par TABULATION (environ 865,000 enregistrements) de la complète des horaires de transport en commun de la ville de Toronto
  • mon appareil d'essai 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 le Code Rapide (/Ot).
  • j'utilise le SQLite "Amalgamation", compilé directement dans mon application test. La version SQLite que j'ai est un peu plus ancienne (3.6.7), mais je soupçonne que ces résultats seront comparables à la dernière version (veuillez laisser un commentaire si vous pensez autrement).

écrivons un peu de code!

le Code: un simple programme C qui lit le fichier texte ligne par ligne, divise la chaîne en valeurs et insère ensuite 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'allons pas réellement insérer des 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] = ""151900920"";

    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] = ""151900920"";

    /*********************************************/
    /* 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 secondsn", n, (clock() - cStartClock) / (double)CLOCKS_PER_SEC);

    sqlite3_close(db);
    return 0;
}

"Contrôle"

exécuter le code as-is n'effectue en fait aucune opération de base de données, mais cela nous donnera une idée de la vitesse à laquelle le fichier c brut I/O et le traitement des chaînes de caractères les opérations sont.

Importés 864913 enregistrements dans 0.94 secondes

Super! Nous pouvons faire 920 000 inserts par seconde, à condition que nous ne fassions pas d'inserts: -)


Le "Scénario De La Pire Éventualité"

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 parce que le SQL sera compilé dans le code VDBE pour chaque insert et chaque insert se produira dans sa propre transaction. à quel point?

Importés 864913 enregistrements dans 9933.61 secondes

Beurk! 2 heures et 45 minutes! C'est seulement 85 inserts par seconde.

utilisant une Transaction

par défaut, SQLite évaluera chaque INSERT / UPDATE statement dans le cadre d'une transaction unique. Si vous exécutez un grand nombre d'inserts, 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és 864913 enregistrements dans 38.03 secondes

c'est mieux. Simplement envelopper tous nos inserts dans une seule transaction a amélioré nos performances à 23.000 inserts par seconde.

utilisant une déclaration préparée

utiliser une transaction était une énorme amélioration, mais recompiler L'instruction SQL pour chaque insert n'a pas de sens si nous utilisons le même SQL over-and-over. Utilisons sqlite3_prepare_v2 pour compiler notre déclaration SQL une fois et puis lions nos paramètres à cette déclaration 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 secondsn", n, (clock() - cStartClock) / (double)CLOCKS_PER_SEC);

sqlite3_finalize(stmt);
sqlite3_close(db);

return 0;

Importés 864913 enregistrements dans 16.27 deuxième

Nice! 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é notre performance à 53,000 inserts par seconde.

PRAGMA synchrone = OFF

par défaut, SQLite fera une pause après avoir émis une commande d'écriture au niveau OS. Cela garantit que les données sont écrites sur le disque. En réglant synchronous = OFF , nous vous SQLite de simplement passer les données à L'OS pour l'écriture et puis continuer. Il y a une chance que le fichier de base de données puisse devenir corrompu si l'ordinateur souffre d'un accident catastrophique (ou une panne de courant) avant que les données 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és 864913 enregistrements dans 12.41 secondes

les améliorations sont maintenant plus petites, mais nous sommes jusqu'à 69,600 inserts par seconde.

PRAGMA journal_mode = MEMORY

envisagez de stocker le journal en mémoire en évaluant PRAGMA journal_mode = MEMORY . Votre transaction sera plus rapide, mais si vous perdez du pouvoir ou si votre programme s'écrase au cours d'une transaction, votre base de données pourrait ê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és 864913 enregistrements dans 13.50 secondes

Un peu plus lent que l'optimisation précédente à 64.000 inserts 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 d'accident), mais nous ne faisons qu'importer des données (pas de 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és 864913 enregistrements dans 12.00 deuxième

fantastique! On peut faire 72 000 inserts par seconde.

utilisant une base de données en mémoire

juste pour le plaisir, nous allons construire sur toutes les optimisations précédentes et redéfinir le nom de fichier de la base de données donc nous travaillons entièrement en RAM:

#define DATABASE ":memory:"

Importés 864913 enregistrements dans 10.94 secondes

Il n'est pas très pratique de stocker notre base de données en RAM, mais c'est impressionnant que nous puissions effectuer 79 000 inserts par seconde.

Refactoring C Code

bien que ce ne soit pas spécifiquement une amélioration SQLite, Je n'aime pas les opérations supplémentaires char* dans la boucle while . Nous allons rapidement remanier ce code pour passer la sortie de strtok() directement dans sqlite3_bind_text() , et laisser le compilateur essayer de vitesse 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 sommes de retour à l'aide d'un véritable fichier de base de données. Les bases de données en mémoire sont rapides, mais pas nécessairement pratiques

Importés 864913 enregistrements dans 8.94 secondes

un léger remaniement au code de traitement de chaîne utilisé dans notre liaison de paramètre nous a permis d'effectuer 96,700 inserts par seconde. I pense qu'il est sûr de dire que c'est très rapide . Comme nous commençons à modifier d'autres variables (i.e. taille de page, Création d'index, etc.) ce sera notre référence.


résumé (jusqu'à présent)

j'espère que vous êtes toujours avec moi! la raison pour laquelle nous avons commencé sur cette route est que la performance des inserts en vrac varie tellement avec SQLite, et il n'est pas toujours évident quels changements doivent être faits pour accélérer notre fonctionnement. En utilisant le même compilateur (et les options de 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 d'un scénario du pire de 85 inserts par seconde à plus de 96.000 inserts par seconde!


créer L'INDEX puis insérer vs Insérer puis créer L'INDEX

avant de commencer à mesurer la performance SELECT , nous savons que nous allons créer des index. Il a été suggéré dans l'une des réponses ci-dessous que lorsque vous faites des inserts en vrac, il est plus rapide de créer l'index après que les données ont été insérées (au lieu de créer l'index d'abord puis d'insérer les données). Essayons:

créer L'Index puis insérer les données

sqlite3_exec(db, "CREATE  INDEX 'TTC_Stop_Index' ON 'TTC' ('Stop')", NULL, NULL, &sErrMsg);
sqlite3_exec(db, "BEGIN TRANSACTION", NULL, NULL, &sErrMsg);
...

Importés 864913 enregistrements dans 18.13 secondes

Insérez les données puis créez L'Index

...
sqlite3_exec(db, "END TRANSACTION", NULL, NULL, &sErrMsg);
sqlite3_exec(db, "CREATE  INDEX 'TTC_Stop_Index' ON 'TTC' ('Stop')", NULL, NULL, &sErrMsg);

Importés 864913 enregistrements dans 13.66 secondes

Comme prévu, en vrac-inserts sont plus lents 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 sans indice est de 96 000 inserts par seconde. la création de l'indice d'abord puis l'insertion des données nous donne 47.700 inserts par seconde, alors que l'insertion des données d'abord, puis la création de l'index nous donne 63.300 inserts par seconde.


je prendrais volontiers des suggestions pour d'autres scénarios à essayer... Et compilera bientôt des données similaires pour des requêtes sélectionnées.

2621
demandé sur Mike Willekes 2009-11-11 01:16:43

9 réponses

plusieurs conseils:

  1. insérez/mettez à jour une transaction.
  2. pour les versions plus anciennes de SQLite-considérez un mode journal Moins paranoïaque ( pragma journal_mode ). Il y a NORMAL , et puis il y a OFF , qui peut augmenter significativement la vitesse d'insertion si vous n'êtes pas trop inquiet que la base de données soit éventuellement corrompue si le système d'exploitation tombe en panne. Si votre application crash les données devraient être très bien. Notez que dans les versions plus récentes, les réglages OFF/MEMORY ne sont pas sûrs pour les accidents au niveau de l'application.
  3. jouer avec les tailles de page fait également une différence ( PRAGMA page_size ). Avoir de plus grandes tailles de page peut faire des lectures et des Écritures aller un peu plus vite que les pages plus grandes sont tenues en mémoire. Notez que plus la mémoire sera utilisée pour votre base de données.
  4. si vous avez des indices, pensez à appeler CREATE INDEX après avoir fait toutes vos insertions. Cela est nettement plus rapide que la création de l'indice et puis je fais tes inserts.
  5. vous devez être très prudent si vous avez un accès simultané à SQLite, car toute la base de données est verrouillée lorsque les Écritures sont faites, et bien que plusieurs lecteurs soient possibles, les Écritures seront verrouillées. Ceci a été quelque peu amélioré avec l'ajout d'un WAL dans les nouvelles versions de SQLite.
  6. profitez-en pour économiser de l'espace...les petites bases de données, plus rapidement. Par exemple, si vous avez des paires de valeurs clés, essayez de faire de la clé un INTEGER PRIMARY KEY si possible, qui remplacera la colonne du numéro de ligne unique implicite dans le tableau.
  7. si vous utilisez plusieurs threads, vous pouvez essayer d'utiliser le cache de page partagée , qui permettra aux pages chargées d'être partagées entre les threads, ce qui peut éviter les appels d'e/s coûteux.
  8. N'utilisez pas !feof(file) !

j'ai aussi posé des questions similaires ici et ici .

682
répondu Snazzer 2018-06-04 15:07:47

essayez d'utiliser SQLITE_STATIC au lieu de SQLITE_TRANSIENT pour ces inserts.

SQLITE_TRANSIENT obligera SQLite à copier les données de la chaîne avant de revenir.

SQLITE_STATIC lui dit que l'adresse mémoire que vous lui avez donnée sera valide jusqu'à ce que la requête ait été effectuée (ce qui dans cette boucle est toujours le cas). Vous économiserez ainsi plusieurs opérations d'allocation, de copie et de désallocation par boucle. Probablement une grande amélioration.

108
répondu Alexander Farber 2015-08-28 16:54:11

éviter les doublures sqlite3_clear_bindings (stmt);

le code dans le test fixe les reliures à chaque fois, ce qui devrait être suffisant.

L'API C intro de l'SQLite docs dit

avant d'appeler sqlite3_step () pour la première fois ou immédiatement après sqlite3_reset(), l'application peut invoquer l'un de l' sqlite3_bind () interfaces pour attacher des valeurs aux paramètres. Chacun l'appel à sqlite3_bind () l'emporte sur les liaisons préalables sur le même paramètre

(voir: sqlite.org/cintro.html ). Il n'y a rien dans les docs pour cette fonction disant que vous devez l'appeler en plus de simplement mettre les fixations.

plus de détails: http://www.hoogli.com/blogs/micro/index.html#Avoid_sqlite3_clear_bindings ()

85
répondu ahcox 2012-08-08 14:44:20

insertions

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 Git dépôt:

https://github.com/rdpoor/CreateOrUpdate

qui charge en vrac un réseau de MySQL , SQLite or PostgreSQL databases. Il inclut une option pour ignorer les enregistrements existants, les écraser ou soulever une erreur. Mes points de repère rudimentaires montrent une amélioration de 10x de la vitesse 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 je suis assez heureux avec elle.

49
répondu fearless_fool 2017-05-23 12:02:48

en Vrac des importations semble fonctionner mieux si vous pouvez découper votre INSÉRER/mettre à JOUR des déclarations. Une valeur d'environ 10.000 a bien fonctionné pour moi sur une table avec seulement quelques rangs, YMMV...

42
répondu Leon 2012-02-21 08:30:20

si vous vous souciez seulement de la lecture, la version un peu plus rapide (mais pourrait lire des données périmées) est de lire à partir de multiples connexions à partir de plusieurs threads (connexion par thread).

trouver les produits, dans le tableau:

 SELECT COUNT(*) FROM table

lire alors en pages (limite / OFFSET)

  SELECT * FROM table ORDER BY _ROWID_ LIMIT <limit> OFFSET <offset>

où et sont calculés par fil, comme ceci:

int limit = (count + n_threads - 1)/n_threads;

pour chaque fil:

int offset = thread_index * limit

pour notre petit (200 Mo) db cela a fait 50-75% d'accélération (3.8.0.2 64 bits sur Windows 7). Nos tableaux sont fortement non normalisés (1 000 à 1 500 colonnes, environ 100 000 rangées ou plus).

trop de fils ou trop peu ne le fera pas, vous devez benchmark et le profil vous-même.

aussi pour nous, SHAREDCACHE a rendu la performance plus lente, donc j'ai mis manuellement PRIVATECACHE (car il a été activé globalement pour nous)

34
répondu malkia 2014-06-23 05:49:58

je coudn'pas obtenir gain de transactions jusqu'à ce que j'ai soulevé cache_size à une valeur plus élevée, c'est à dire PRAGMA cache_size=10000;

22
répondu anefeletos 2015-04-15 09:47:49

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 a environ 30 millions d'enregistrements. J'utilise la même configuration que vous suggérez mais mon nombre d'INSERTs 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 un seul insert sans erreur/Échec. Disons que vous sont en train de diviser chaque enregistrement en plusieurs inserts sur différentes tables. Qu'advient-il si l'enregistrement est cassé?

la commande on CONFLICT ne s'applique pas, parce que 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 a une erreur de contrainte, alors tous les 4 inserts précédents doivent y aller aussi.

donc voici où le rollback vient. Le seul problème avec le rollback est que vous perdez tous vos inserts et commencer à partir du haut. Comment pouvez-vous résoudre ce problème?

ma solution était d'utiliser transactions multiples . Je commence et je 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 taille 10.000 et y insérer les enregistrements réussis. Lorsque l'erreur se produit, je fais un retour en arrière, commence une transaction, insère les enregistrements de mon tableau, commit puis commence une nouvelle transaction après le disque cassé.

Cette solution m'a aidé à contourner les problèmes que j'ai lorsqu'il s'agit de fichiers contenant de mauvais enregistrements ou des doublons (j'avais presque 4% de mauvais enregistrements).

l'algorithme que j'ai créé m'a aidé à réduire mon processus de 2 heures. Processus de chargement Final du fichier 1hr 30m 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 d'accélérer, je suis ouvert à toutes les suggestions.

mise à JOUR :

en plus de ma réponse ci-dessus, vous devriez garder à l'esprit que les inserts par seconde selon le disque dur que vous utilisez aussi. Je l'ai testé sur 3 PC différents avec des disques durs différents et j'ai eu des différences énormes dans les temps. PC1 (1h 30m), PC2 (6h) PC3 (14h), alors j'ai commencé à me demander pourquoi.

après deux semaines de recherche et de vérification multiple ressources: disque dur, Ram, Cache, j'ai découvert que certains paramètres sur votre disque dur peut affecter le taux D'entrée/sortie. En cliquant propriétés sur votre lecteur de sortie désiré, vous pouvez voir deux options dans l'onglet Général. Opt1: compresse ce lecteur, Opt2: permet aux fichiers de ce lecteur d'avoir des contenus indexés.

en désactivant ces deux options, les 3 PC prennent maintenant à peu près le même temps de terminer (1h et 20 à 40min). Si vous rencontrez des inserts lents vérifier si votre disque dur est configuré avec ces options. Cela vous fera gagner beaucoup de temps et des maux de tête en essayant de trouver la solution

14
répondu Jimmy_A 2018-01-06 11:07:06

la réponse à votre question Est que le nouveau sqlite3 a amélioré les performances, utilisez cela.

cette réponse pourquoi SQLAlchemy insert avec sqlite 25 fois plus lent que l'utilisation de sqlite3 directement? par SqlAlchemy Orm Auteur a 100k insère dans les 0,5 secondes, et j'ai vu des résultats similaires avec python-sqlite et SqlAlchemy. Ce qui me porte à croire que la performance s'est améliorée avec sqlite3

6
répondu doesnt_matter 2017-06-15 20:31:47