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).
  • Wie man Ergebnisse aus einer sqlite3-Abfrage auf getrennten Zeilen in einer tkinter-GUI ausgibt
  • Platzhalter für Tabellenname
  • Probleme mit python 2.7.3 auf Centos mit sqlite3 Modul
  • Warum mein Python sieht nicht pysqlite?
  • SQLite 3: sqlite InterfaceError: Fehlerbindungsparameter 0 - wahrscheinlich nicht unterstützter Typ
  • Python Kopie MySQL-Tabelle auf SQLite3
  • Problem beim Speichern von numpy Array in sqlite3 mit Python
  • Wie man eine Tabelle durch den Unterschied in Prozent zwischen Preisen zu verschiedenen Datum zu sortieren
  • Mit Sqlite3 auf Heroku Cedar Stack
  • So aktualisieren Sie sqlite3 in Python 2.7.3 in einem virtualenv?
  • Programmierfehler: Falsche Anzahl der gelieferten Bindungen
  • Python ist die beste Programmiersprache der Welt.