Profil Millionen von Textdateien im Parallel mit einem Sqlite Counter?

Ein Berg von Textdateien (von Typen A, B und C) sitzt auf meiner Brust, langsam, kalt verweigert mich dringend benötigte Luft. Im Laufe der Jahre hat jede Art spec hat Verbesserungen, so dass die gestrige typeA-Datei hat viele weitere Eigenschaften als letztes Jahr TypA. Um einen Parser zu bauen, der die lange Evolution dieser Dateitypen des Jahrzehnts verarbeiten kann, ist es sinnvoll, alle 14 Millionen von ihnen iterativ, ruhig, aber vor dem Sterben unter ihrem zerquetschenden Gewicht zu inspizieren.

Ich baute einen laufenden Zähler, so dass jedes Mal, wenn ich Eigenschaften (vertraut oder nicht) Ich füge 1 zu seinem Tally. Die sqlite Tally Board sieht so aus:

Bildbeschreibung hier eingeben

In der besonderen Veranstaltung sehe ich eine fremde Eigenschaft, die ich dem Tally hinzufüge. Auf einer typeA-Datei, die wie folgt aussieht:

Bildbeschreibung hier eingeben

Ich habe dieses System herunter! Aber es ist langsam @ 3M Dateien / 36 Stunden in einem Prozess. Ursprünglich war ich mit diesem Trick , um sqlite eine Liste von Eigenschaften, die Inkrementierung passieren.

 placeholder= '?' # For SQLite. See DBAPI paramstyle. placeholders= ', '.join(placeholder for dummy_var in properties) sql = """UPDATE tally_board SET %s = %s + 1 WHERE property IN (%s)""" %(type_name, type_name, placeholders) cursor.execute(sql, properties) 

Ich habe gelernt, das ist eine schlechte Idee, weil

  1. sqlite ist viel langsamer als indizierte Suche
  2. Mehrere Hunderte von Eigenschaften (ca. 160 Zeichen lang) machen für wirklich lange SQL-Abfragen
  3. Mit %s anstelle von ? Ist schlechte Sicherheitspraxis … (kein Anliegen ATM)

Ein "Fix" war, eine Skriptseiteneigenschaft aufrechtzuerhalten – rowid hash der Tally, die in dieser Schleife verwendet wird:

  1. Datei für new_properties
  2. tally_board für rowid , property
  3. Generiere Skriptseite client_hash von 2's lesen
  4. Schreiben Sie Zeilen auf tally_board für jede new_property nicht in property (nichts inkrementiert noch). Aktualisieren Sie client_hash mit neuen Eigenschaften
  5. Lookup rowid für jede Zeile in new_properties mit dem client_hash
  6. Schreiben Sie Inkrement zu jedem rowid (jetzt ein Proxy für property ) zu tally_board

Schritt 6. sieht aus wie

 sql = """UPDATE tally_board SET %s = %s + 1 WHERE rowid IN %s""" %(type_name, type_name, tuple(target_rows)) cur.execute 

Das Problem damit ist

  • Es ist immer noch langsam!
  • Es manifestiert eine Racebedingung in der Parallelverarbeitung, die Duplikate in der property einführt, wenn threadA Schritt 2 beginnt, bevor threadB Schritt 6 abgeschlossen hat.

Eine Lösung für die Race-Bedingung ist, um die Schritte 2-6 eine exklusive Sperre auf die db, obwohl es nicht aussehen wie Liest können diese Lock A Read .

Ein weiterer Versuch verwendet eine echte UPSERT um bereits vorhandene property UPSERT und neue property einzufügen (und inkrementieren).

Vielleicht gibt es Glück in so etwas wie ich, aber ich bin mir nicht sicher, wie man es umschreibt, um die Tally zu erhöhen.

2 Solutions collect form web for “Profil Millionen von Textdateien im Parallel mit einem Sqlite Counter?”

Wie wäre es mit einer Änderung des Tabellenschemas? Anstelle einer Spalte pro Typ haben Sie eine Spalte. Dann haben Sie eindeutige Zeilen, die von Eigenschaft und Typ identifiziert wurden, wie folgt:

 |rowid|prop |type |count| ============================ |1 |prop_foo|typeA|215 | |2 |prop_foo|typeB|456 | 

Dies bedeutet, dass Sie eine Transaktion für jede Eigenschaft jeder Datei separat eingeben können und lassen Sie sqlite Sorgen um Rennen. Also für jede Eigenschaft, die Sie begegnen, sofort eine vollständige Transaktion, die die nächste Summe berechnet und upserts der Datensatz identifiziert durch den Namen der Eigenschaft und Dateityp.

Die folgenden bescheiden sich immens:

  • Schrieb weniger oft an SQLite . Halten die meisten meiner Zwischenergebnisse in Speicher dann Aktualisierung der DB mit ihnen alle 50k Dateien führte zu etwa einem Drittel der Ausführungszeit (35 Stunden bis 11,5 Stunden)
  • Bewegen von Daten auf meinen PC (aus irgendeinem Grund mein USB3.0-Port war die Übertragung von Daten weit unter USB2.0-Raten). Dies führte zu einem Fünftel der Ausführungszeit (11,5 Stunden bis 2,5 Stunden).
  • Holen Sie die Anzahl der modifizierten Zeilen nach sqlite3 ausführen
  • SQLite3 Python: executemany SELECT
  • Python sqlite3 String Variable in execute
  • Ganzes JSON in ein SQLite-Feld mit Python
  • Öffnen von sqlite3-Datenbank aus python im schreibgeschützten Modus
  • Wie man Werte von zwei verschiedenen sqlite3 Tabellen in python subtrahiert
  • Wie kann man die Existenz einer Zeile in SQLite mit Python überprüfen?
  • Dump-Daten aus fehlerhaftem SQLite in Python
  • Python-Wrapper verhindert Funktionsaufruf?
  • Sqlite - Verwenden Sie Backticks (`) oder doppelte Anführungszeichen (") mit Python
  • Einrichten der Konfiguration der sqlite3-Datenbank in Python SQLITE_CONFIG_MULTITHREAD
  • Python ist die beste Programmiersprache der Welt.