Verbesserung der EINSATZ-pro-Sekunde-Leistung von SQLite?
Optimierung von SQLite ist schwierig. Bulk-insert performance einer C-Anwendung kann sich variieren von 85 Einsätzen pro Sekunde auf über 96.000 inserts pro Sekunde!
Hintergrund: Wir sind mit SQLite als Teil einer desktop-Anwendung. Wir haben große Mengen von Konfigurationsdaten in XML-Dateien gespeichert, analysiert und geladen, in eine SQLite-Datenbank für die weitere Verarbeitung, wenn die Anwendung initialisiert wird. SQLite ist ideal für diese situation, denn es ist schnell, es erfordert keine spezielle Konfiguration und die Datenbank gespeichert ist auf der Festplatte als eine einzige Datei.
Begründung: Zunächst war ich enttäuscht, mit der Leistung, die ich sah. Es stellt sich heraus, dass die performance von SQLite erheblich variieren kann (sowohl für bulk-inserts und selects), je nachdem, wie die Datenbank konfiguriert ist und wie du mit der API. War es nicht eine triviale Angelegenheit, um herauszufinden, was all die Optionen und Techniken, so dass ich dachte, es klug, zu schaffen, das community-wiki-Eintrag an teilen der Ergebnisse mit Stapel - Overflow Leser, um andere zu retten die Mühe, die gleichen Untersuchungen.
Das Experiment:, Anstatt einfach zu reden über die performance-Tipps im Allgemeinen Sinne (d.h. "mit einer Transaktion!"), Ich dachte, es am besten schreiben C-code und eigentlich Messen auf die Auswirkungen der verschiedenen Optionen. Beginnen wir mit einigen einfachen Daten:
- Eine 28 MB Datei durch Tabulatoren getrennter text (etwa 865,000 records) der komplette transit-Zeitplan für die Stadt Toronto
- Meine test-Maschine ist eine 3.60 GHz P4 läuft Windows XP.
- Der code kompiliert wird mit Visual C++ 2005 als "Release" mit "Full Optimization" (/Ox) und Zugunsten Schnellen Code (/Ot).
- Ich bin mit dem SQLite "Verschmelzung", kompiliert direkt in meiner test Anwendung. Die SQLite-version ich gerade habe, ist ein bisschen älter (3.6.7), aber ich vermute, dass diese Ergebnisse vergleichbar mit der aktuellen Version (bitte einen Kommentar hinterlassen, wenn Sie anders denken).
Schreiben wir etwas code!
Code: Einem einfachen C-Programm, das liest die Textdatei Zeile für Zeile, teilt den string in die Werte und dann fügt die Daten in eine SQLite-Datenbank. In dieser "baseline" - version des Codes, der Datenbank erstellt wird, aber wir werden nicht wirklich Daten einfügen:
/*************************************************************
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;
}
Die "Control" -
Läuft der code ist nicht wirklich ausführen, Datenbank-Operationen, aber es gibt uns eine Vorstellung davon, wie schnell die raw-C Datei-I/O-und string-Verarbeitung sind.
Importiert 864913 Datensätze in 0.94
Sekunden
Toll! Wir können tun, 920,000 inserts pro Sekunde, zur Verfügung gestellt, die wir überhaupt nicht tun, keine Beilagen 🙂
Das "Worst-Case-Szenario"
Gehen wir zum generieren der SQL-string mit den Werten aus der Datei Lesen und den Aufruf der SQL-operation mit 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);
Dieser wird langsam sein, weil der SQL erstellt werden, in VDBE code für jede insert und jede insert-geschehen wird in einer eigenen Transaktion. Wie langsam?
Importiert 864913 Datensätze in 9933.61
Sekunden
Huch! 2 Stunden und 45 Minuten! Das ist nur 85 Einsätzen pro Sekunde.
Über eine Transaktion
Standardmäßig SQLite wertet jede INSERT - /UPDATE-Anweisung in einer einzigen Transaktion. Wenn Sie eine große Anzahl von Einsätzen, ist es ratsam, wickeln Sie Ihre operation in einer Transaktion:
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);
Importiert 864913 Datensätze in 38.03
Sekunden
Besser. Einfach wickeln alle unsere Einsätze in einer einzigen Transaktion verbessert unsere Leistung zu als 23.000 Einsätzen pro Sekunde.
Mit einer Vorbereiteten Anweisung
Mithilfe einer Transaktion war eine riesige Verbesserung, aber zum erneuten kompilieren der SQL-Anweisung für jede insert-macht keinen Sinn, wenn wir mit dem gleichen SQL-über-und-über. Wir verwenden sqlite3_prepare_v2
zum kompilieren der SQL-Anweisung einmal und dann binden unsere Parameter an diese Anweisung mit 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;
Importiert 864913 Datensätze in 16.27
Sekunden
Schön! Es ist ein wenig mehr code (vergessen Sie nicht, rufen sqlite3_clear_bindings
und sqlite3_reset
), aber wir haben uns mehr als verdoppelt, unsere Leistung zu 53,000 inserts pro Sekunde.
PRAGMA synchronous = OFF
Standardmäßig SQLite pause machen wird nach der Ausgabe einer OS-level write-Befehl. Dies garantiert, dass die Daten auf die Festplatte geschrieben. Durch die Einstellung synchronous = OFF
wir sind angewiesen SQLite einfach zu hand-off-Daten, um das Betriebssystem zu schreiben und dann weiter. Es gibt eine chance, dass die Datenbank-Datei möglicherweise beschädigt, wenn der computer erleidet einen katastrophalen Absturz (oder Stromausfall), bevor die Daten geschrieben, um die Platte:
/* 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);
Importiert 864913 Datensätze in 12.41
Sekunden
Verbesserungen sind jetzt kleiner, aber wir sind bis zu 69,600 inserts pro Sekunde.
PRAGMA journal_mode = SPEICHER
Betrachten speichern Sie die rollback-journal im Speicher durch die Auswertung PRAGMA journal_mode = MEMORY
. Ihre Transaktion wird schneller sein, aber wenn Sie macht verlieren oder Ihr Programm abstürzt, während eine Transaktion, die Sie könnte die Datenbank werden Links in einem korrupten Staat mit einer teilweise abgeschlossenen Transaktion:
/* 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);
Importiert 864913 Datensätze in 13.50
Sekunden
Etwas langsamer als die Vorherige Optimierung bei 64,000 inserts pro Sekunde.
PRAGMA synchronous = OFF und PRAGMA journal_mode = SPEICHER
Let ' s kombinieren Sie die beiden vorherigen Optimierungen. Es ist ein wenig mehr riskant (bei einem crash), aber wir sind nur das importieren von Daten (nicht mit einer bank):
/* 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);
Importiert 864913 Datensätze in 12.00
Sekunden
Fantastisch! Wir sind in der Lage, das zu tun 72,000 inserts pro Sekunde.
Mit einem In-Memory-Datenbank
Nur zum Spaß, wir bauen auf den vorherigen Optimierungen und definieren Sie die Datenbank mit dem Namen, also wir arbeiten vollständig im RAM:
#define DATABASE ":memory:"
Importiert 864913 Datensätze in 10.94
Sekunden
Es ist nicht super-praktisch zum verstauen unsere Datenbank im RAM, aber es ist beeindruckend, dass wir durchführen können 79,000 inserts pro Sekunde.
Refactoring Von C-Code
Obwohl nicht ausdrücklich eine SQLite-Verbesserungen, ich weiß nicht, wie die extra char*
Zuordnung der Operationen in den while
Schleife. Lassen Sie uns schnell umgestalten, dass code zu übergeben, die Ausgabe von strtok()
direkt in sqlite3_bind_text()
, und lassen Sie die compiler versuchen, die Dinge zu beschleunigen für uns:
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);
Hinweis: Wir sind zurück in der Verwendung einer echten Datenbank-Datei. In-memory-Datenbanken sind schnell, aber nicht unbedingt praktisch
Importiert 864913 Datensätze in 8.94
Sekunden
Einer leichten Umgestaltung, um die string-Verarbeitung-code in unserem parameter-Bindung hat es uns erlaubt, zu führen 96,700 inserts pro Sekunde. Ich denke, es ist sicher zu sagen, dass dies reichlich schnell. Als wir beginnen, zu zwicken, andere Variablen (D. H. die Größe der Seite, index-Erstellung, etc.) das wird unser Maßstab.
Zusammenfassung (bisher)
Ich hoffe, du bist immer noch mit mir! Der Grund, warum wir Schritte auf diesem Weg ist, dass bulk-insert-Leistung variiert so Wild mit SQLite, und es ist nicht immer klar, welche änderungen vorgenommen werden müssen, zu beschleunigen-unsere Bedienung. Mit dem gleichen compiler (und auch compiler-Optionen), die gleiche version von SQLite und die gleichen Daten, die wir optimiert haben unseren code und unsere Verwendung von SQLite zu gehen von einem worst-case-Szenario von 85 Einsätzen pro Sekunde auf über 96.000 inserts pro Sekunde!
CREATE INDEX dann EINFÜGEN vs. EINFÜGEN dann CREATE INDEX
Bevor wir mit der Messung zu beginnen SELECT
Leistung, wissen wir, dass wir die Erstellung von Indizes. Es ist vorgeschlagen worden, in einem der nachfolgenden Antworten Sie, dass bei Massen-inserts, es ist schneller, um den index zu erstellen, nachdem die Daten eingefügt wurden (im Gegensatz zu der Erstellung der index zuerst, dann das einfügen der Daten). Lassen Sie uns versuchen:
Create Index-dann Einfügen von Daten
sqlite3_exec(db, "CREATE INDEX 'TTC_Stop_Index' ON 'TTC' ('Stop')", NULL, NULL, &sErrMsg);
sqlite3_exec(db, "BEGIN TRANSACTION", NULL, NULL, &sErrMsg);
...
Importiert 864913 Datensätze in 18.13
Sekunden
Einfügen von Daten Erstellen Sie dann den Index
...
sqlite3_exec(db, "END TRANSACTION", NULL, NULL, &sErrMsg);
sqlite3_exec(db, "CREATE INDEX 'TTC_Stop_Index' ON 'TTC' ('Stop')", NULL, NULL, &sErrMsg);
Importiert 864913 Datensätze in 13.66
Sekunden
Als erwartet, bulk-inserts werden langsamer, wenn einen Spalte indiziert ist, aber es macht schon einen Unterschied, wenn der index erstellt wird, nachdem die Daten eingefügt werden. Unsere no-index baseline 96,000 inserts pro Sekunde. Erstellen der index zuerst, dann einfügen von Daten gibt uns 47,700 inserts pro Sekunde, während das einfügen der Daten erst dann erstellen der index gibt uns 63,300 inserts pro Sekunde.
Ich würde gerne mal Vorschläge für andere Szenarien, um zu versuchen... Und kompilieren Sie ähnliche Daten für SELECT-Abfragen schnell.
"Wir haben große Mengen von Konfigurationsdaten in XML-Dateien gespeichert, analysiert und geladen, in eine SQLite-Datenbank für die weitere Verarbeitung, wenn die Anwendung initialisiert wird." warum nicht Sie halten alles, was in der sqlite-Datenbank in den ersten Platz, statt der Speicherung in XML und lädt dann alles bei der Initialisierung Zeit?
Haben Sie versucht, den Aufruf nicht
sqlite3_clear_bindings(stmt);
? Legen Sie die Bindungen jedes mal durch, das sollte reichen: Vor dem Aufruf von sqlite3_step() für die erste Zeit oder sofort nach sqlite3_reset () kann die Anwendung aufrufen einer der sqlite3_bind () - Schnittstellen zum befestigen von Werten zu den Parametern. Jeder Aufruf sqlite3_bind() überschreibt die Vorherige Bindungen, die die gleichen parameter (siehe: sqlite.org/cintro.html). Es gibt nichts in der docs für diese Funktion sagen Sie müssen es nennen.ahcox: die Bindung ist an die Adresse am spitz und nicht die variable, so dass nicht funktionieren würde, da
strtok
gibt einen neuen Zeiger jedes mal. Sie würde entweder strcpy
nach jedem strtok
oder machen Sie Ihre eigenen tokenizer, die immer kopiert, wie es liest entlang der saite.Hast du wiederholten Messungen? Das 4s "win" zur Vermeidung von 7 lokalen Zeigern ist seltsam, selbst unter der Annahme einer verwirrten optimizer.
InformationsquelleAutor |
Du musst angemeldet sein, um einen Kommentar abzugeben.
Einige Tipps:
pragma journal_mode
). Es istNORMAL
, und dann gibt esOFF
, erheblich erhöhen können legen Sie die Geschwindigkeit, wenn Sie nicht allzu besorgt über die Datenbank möglicherweise beschädigt, wenn das OS abstürzt. Wenn Ihre Anwendung abstürzt, die Daten sollten in Ordnung sein. Beachten Sie, dass in neueren Versionen, dieOFF/MEMORY
Einstellungen sind nicht sicher für die Anwendung level abstürzt.PRAGMA page_size
). Mit größeren Seitengrößen machen kann, liest und schreibt ein bisschen schneller als größere Seiten werden im Speicher gehalten. Beachten Sie, dass mehr Speicherplatz für Ihre Datenbank.CREATE INDEX
nachdem Sie alle Ihre Einsätze. Dies ist deutlich schneller als das erstellen des Indexes und dann tun Sie Ihre Einsätze.INTEGER PRIMARY KEY
wenn möglich, an die Stelle der implizierten eindeutigen Zeilen-Nummer-Spalte in der Tabelle.!feof(Datei)
!Habe ich mich auch gefragt, ähnliche Fragen hier und hier.
Es ist schon eine Weile, meine Vorschläge für ältere Versionen vor einem WAL eingeführt wurde. Sieht aus wie LÖSCHEN, wird die neue Einstellung "normal", und jetzt gibt es AB und SPEICHER-Einstellungen als gut. Ich nehme an, OFF/SPEICHER Verbesserung von schreib-performance auf Kosten der Integrität der Datenbank, und OFF deaktiviert die rollbacks komplett.
für die #7, hast du ein Beispiel, wie man aktivieren shared page-cache mit der c# - system.Daten.sqlite-wrapper?
#4 brachte wieder uralte Erinnerungen--Es war zumindest in einem Fall zurück, in der vor-Zeiten, wo das löschen eines index vor einer Gruppe fügt und neu zu erstellen, danach beschleunigt Einsätze deutlich. Kann immer noch arbeiten, schneller auf modernen Systemen für einige Hinzugefügt, wo Sie wissen, Sie haben alleinigen Zugriff auf die Tabelle für den Zeitraum.
InformationsquelleAutor
Versuchen Sie es mit
SQLITE_STATIC
stattSQLITE_TRANSIENT
für diese Einsätze.SQLITE_TRANSIENT
verursachen SQLite kopieren der string-Daten vor der Rückgabe.SQLITE_STATIC
sagt es, dass die Speicher-Adresse, die Sie gab, wird es gültig, bis der query ausgeführt wurde (was in dieser Schleife wird immer der Fall ist). So sparen Sie mehrere allokieren, kopieren und freigeben von Operationen pro Schleife. Möglicherweise eine große Verbesserung.InformationsquelleAutor
Vermeiden sqlite3_clear_bindings(stmt);
Den code in die test-sets die Bindungen jedes mal durch, das sollte genug sein.
Die C-API-intro aus der SQLite-docs, sagt
(siehe: sqlite.org/cintro.html). Es gibt nichts in der Dokumentation für die Funktion sagen, müssen Sie es zusätzlich zum einfachen einstellen der Bindungen.
Detail: http://www.hoogli.com/blogs/micro/index.html#Avoid_sqlite3_clear_bindings()
InformationsquelleAutor
Auf bulk-inserts
Inspiriert von diesem Beitrag und von der Stack-Überlauf Frage, die mich dazu gebracht hier -- Ist es möglich mehrere Zeilen einfügen, in einer Zeit, in eine SQLite-Datenbank? -- ich habe meine erste Git repository:
https://github.com/rdpoor/CreateOrUpdatedie bulk-Ladungen ein array von ActiveRecords in MySQL, SQLite oder PostgreSQL Datenbanken. Es enthält eine option zum ignorieren der vorhandenen Datensätze, überschreiben oder einen Fehler auslösen. Meine rudimentären benchmarks zeigen ein 10x Verbesserung der Geschwindigkeit im Vergleich zu sequenziellen schreiben -- YMMV.
Ich bin mit es in der Produktion code, wo ich Häufig für den import von großen Datenmengen, und ich bin ziemlich glücklich mit ihm.
InformationsquelleAutor
Bulk-Einfuhren scheint zu funktionieren am besten, wenn Sie können chunk Ihre INSERT/UPDATE Aussagen. Ein Wert von 10.000 oder so, hat gut funktioniert für mich auf einer Tabelle mit nur ein paar Zeilen, YMMV...
InformationsquelleAutor
Wenn es dir nur um das Lesen, etwas schneller (aber vielleicht Lesen veralteter Daten) version zu Lesen ist, aus mehreren verbindungen aus mehreren threads (Verbindung pro thread).
Finden Sie zuerst die Elemente in der Tabelle:
Lesen Sie dann in Seiten (LIMIT/OFFSET)
wo und berechnet pro-thread, wie diesem:
für jeden thread:
Für unsere kleine (200mb) db dieses gemacht 50-75% speed-up (3.8.0.2 64-bit auf Windows 7). Unsere Tische sind stark nicht-normalisierten (1000-1500 Spalten, rund 100 ' 000 oder mehr Zeilen).
Zu viele oder zu wenig threads es nicht tun, müssen Sie den Maßstab und das Profil selbst.
Auch für uns, SHAREDCACHE gemacht, die Leistung langsamer, so dass ich manuell PRIVATECACHE (Ursache war es Global aktiviert für uns)
InformationsquelleAutor
Ich coudn ' T erhalten jeden Gewinn, aus Transaktionen, bis ich angesprochen cache_size auf einen höheren Wert, d.h.
PRAGMA cache_size=10000;
InformationsquelleAutor
Nach der Lektüre dieses tutorial, ich habe versucht es zu implementieren, um mein Programm.
Habe ich 4-5 Dateien, die Adressen enthalten. Jede Datei hat ca 30 Millionen Datensätze. Ich benutze die gleiche Konfiguration, die Sie vorschlagen, aber meine Anzahl der Einlagen pro Sekunde ist viel zu niedrig (~10.000 Datensätze pro Sekunde).
Hier ist, wo dein Vorschlag scheitert. Verwenden Sie eine einzige Transaktion für alle Datensätze und einer einzelnen insert ohne Fehler/fehl. Lassen Sie uns sagen, dass Sie die Teilung jeder Datensatz in mehrere Einsätze auf verschiedenen Tischen. Was passiert, wenn der Rekord gebrochen ist?
Den KONFLIKT-Befehl nicht anwenden, verursachen, wenn Sie 10 Elemente in einem Datensatz, und Sie müssen jedes element eingefügt, um zu einer anderen Tabelle, wenn das element 5 wird ein CONSTRAINT-Fehler, dann alle vorherigen 4 Einsätze brauchen, um zu gehen.
So, hier ist, wo die rollback kommt. Das einzige Problem mit dem rollback ist, verlieren Sie alle Ihre Einsätze und beginnen Sie von oben. Wie lösen Sie diese?
Meine Lösung war die Verwendung mehrere Transaktionen. Ich beginne und Ende einer Transaktion alle 10.000 Datensätze (Fragen Sie nicht warum, die Nummer, es war die Schnellste, die ich getestet habe). Ich erstellt ein array der Größe 10.000 und legen Sie die erfolgreichen Platten. Wenn der Fehler Auftritt, mache ich ein rollback eine Transaktion beginnen, legen Sie die Datensätze aus meinem array übernehmen und dann eine neue Transaktion starten, nachdem der Rekord gebrochen.
Diese Lösung hat mir geholfen, umgehen die Probleme, die ich habe, wenn der Umgang mit Dateien, die mit bad/doppelte Datensätze (ich hatte fast 4% negativ).
Den Algorithmus, den ich erstellt mir geholfen reduzieren, meinen Prozess von 2 Stunden. Letzte Ladevorgang der Datei 1H 30m das ist immer noch langsam, aber nicht im Vergleich zu den 4 Stunden, die es zunächst war. Ich konnte die Geschwindigkeit der Einsätze von 10.000/s bis ~14.000/s
Wenn jemand noch andere Ideen, wie es zu beschleunigen, bin ich für Vorschläge offen.
UPDATE:
In Ergänzung zu meiner Antwort oben, Sie sollten im Hinterkopf behalten, dass inserts pro Sekunde, je nach Festplatte, die Sie verwenden, zu. Getestet habe ich es auf 3 verschiedenen PCs mit verschiedenen Festplatten und bekam massive Unterschiede in den Zeiten. PC1 (1H 30m), PC2 (6Std) PC3 (14h insgesamt), so dass ich begann mich zu Fragen, warum das sein würde.
Nach zwei Wochen recherche und überprüfung mehrerer Ressourcen: Festplatte, Ram, Cache, fand ich heraus, dass einige Einstellungen auf der Festplatte kann sich auf die I/O-rate. Indem Sie auf Eigenschaften klicken Sie auf das gewünschte Ausgabe-Laufwerk können Sie sehen zwei Optionen in der Registerkarte allgemein. Opt1: Compress dieses Laufwerk, Opt2: Zulassen, dass für Dateien von diesem Laufwerk Inhalte indiziert.
Durch das deaktivieren dieser beiden Optionen alle 3 PCs nun nehmen Sie etwa die gleiche Zeit bis zum Ende (1 Stunde und 20 bis 40min). Wenn Sie Begegnung langsam fügt überprüfen Sie, ob Ihre Festplatte konfiguriert ist, mit diesen Optionen. Es spart Ihnen viel Zeit und Kopfschmerzen versuchen, um die Lösung zu finden
Zu tun, dass ich in der Lage bin, zu importieren 5,582,642 Datensätze in 11.51 Sekunden
InformationsquelleAutor
Die Antwort auf Ihre Frage ist, dass neuere sqlite3 hat bessere Leistung, verwenden Sie diese.
Diese Antwort Warum ist SQLAlchemy-Einsatz mit sqlite-25 mal langsamer als mit sqlite3 direkt? von SqlAlchemy Orm Autor hat 100k Einsätze in 0,5 sec, und ich habe gesehen, ähnliche Ergebnisse mit python-sqlite und SqlAlchemy. Das führt mich zu glauben, dass Leistung sich verbessert hat mit sqlite3
InformationsquelleAutor
Verwenden ContentProvider für das einfügen der Großteil der Daten in der db.
Die unten verwendete Methode für das einfügen von Massendaten in Datenbanken. Dies sollte Verbesserung der INSERT-pro-Sekunde-Leistung von SQLite.
Call bulkInsert-Methode :
Link: https://www.vogella.com/tutorials/AndroidSQLite/article.html
prüfen Sie Mit ContentProvider Abschnitt für weitere details
InformationsquelleAutor