Eine riesige Datei (80 GB) auf irgendeine Weise zu beschleunigen?

112
 grep -i -A 5 -B 5 'db_pd.Clients'  eightygigsfile.sql

Dies läuft seit einer Stunde auf einem ziemlich leistungsfähigen Linux-Server, der sonst nicht überlastet ist. Irgendeine Alternative zu grep? Irgendetwas an meiner Syntax, das verbessert werden kann (egrep, fgrep besser?)

Die Datei befindet sich tatsächlich in einem Verzeichnis, das für einen Mount auf einem anderen Server freigegeben ist, aber der tatsächliche Speicherplatz ist lokal, sodass dies keinen Unterschied machen sollte.

Der Grep greift nach bis zu 93% der CPU

zzapper
quelle
8
Abhängig von Ihrem Gebietsschema kann der -iSwitch den Prozess verlangsamen. Versuchen Sie es ohne -ioder mit LC_ALL=C grep .... Wenn Sie nur nach einer festen Zeichenfolge suchen, verwenden Sie grep -F.
Thor
5
Als @dogbane mit der erwähnten LC_ALL = C Variable zusammen mit fgrep kann Ihre search.I beschleunigen habe einige Tests und konnte eine erzielen 1400% Leistungssteigerung und schrieb einen ausführlichen Artikel, warum dies in meinem ist bis grep Geschwindigkeit Post
JacobN
Ich bin neugierig - welche Datei ist 80 GB groß? Ich würde gerne glauben, dass es eine bessere Speicherstrategie gibt, wenn eine Datei so groß wird (z. B. Protokolldateien drehen oder hierarchisch in verschiedene Dateien und Ordner kategorisieren). Wenn die Änderungen nur an bestimmten Stellen der Datei auftreten (z. B. am Ende), speichern Sie einfach einige Grep-Ergebnisse aus dem vorherigen Abschnitt, die sich nicht ändern, und greifen Sie statt der ursprünglichen Datei die gespeicherte Ergebnisdatei.
Sridhar Sarnobat
Ich habe mich für github.com/google/codesearch entschieden - sowohl die Indizierung als auch die Suche sind blitzschnell (in Go geschrieben). cindex .um dann Ihren aktuellen Ordner zu indizieren csearch db_pd.Clients.
ccpizza
1
Wenn Ihre Datei indiziert oder sortiert wäre, könnte dies erheblich beschleunigt werden. Das Durchsuchen jeder Zeile ist per Definition O (n), während eine sortierte Datei durch Halbieren gesucht werden kann. An diesem Punkt würden Sie weniger als eine Sekunde sprechen, um Ihre 80 GB zu durchsuchen (daher benötigt eine indizierte 80 GB-Datenbank überhaupt keine Zeit für ein einfaches SELECT, während dein grep ... naja, solange es dauert).
Charles Duffy

Antworten:

147

Hier sind einige Optionen:

1) Stellen Sie Ihrem Befehl grep das Präfix vor LC_ALL=C, um das Gebietsschema C anstelle von UTF-8 zu verwenden.

2) Verwenden fgrepSie diese Option, weil Sie nach einer festen Zeichenfolge suchen, nicht nach einem regulären Ausdruck.

3) Entfernen Sie die -iOption, wenn Sie sie nicht benötigen.

So wird Ihr Befehl:

LC_ALL=C fgrep -A 5 -B 5 'db_pd.Clients' eightygigsfile.sql

Es ist auch schneller, wenn Sie Ihre Datei auf die RAM-Disk kopieren.

Dogbane
quelle
5
das war um eine Größenordnung viel schneller, danke. Übrigens habe ich -n hinzugefügt, um die Zeilennummern zu erhalten. Auch vielleicht ein -m nach dem Spiel zu
beenden
5
Wow, vielen Dank @dogbane, toller Tipp! Dies führte mich durch einen Forschungstunnel, um herauszufinden, warum LC_ALL = C grep beschleunigt, und es war eine sehr aufschlussreiche Erfahrung!
JacobN
7
Einige Leute (nicht ich) mögen grep -Fmehr alsfgrep
Walter Tross
2
Mein Verständnis ist, dass LANG=C(statt LC_ALL=C) genug ist und einfacher zu tippen ist.
Walter Tross
2
@Adrian fgrepist eine andere Art zu schreiben grep -F, wie man fgrepSie sehen werden. Einige Versionen des mansagen auch, dass das erstere für das letztere veraltet ist, aber die kürzere Form ist zu bequem, um zu sterben.
Walter Tross
36

Wenn Sie eine Multicore-CPU haben, würde ich GNU parallel wirklich empfehlen . Um eine große Datei parallel zu erfassen, verwenden Sie:

< eightygigsfile.sql parallel --pipe grep -i -C 5 'db_pd.Clients'

Abhängig von Ihren Festplatten und CPUs kann das Lesen größerer Blöcke schneller sein:

< eightygigsfile.sql parallel --pipe --block 10M grep -i -C 5 'db_pd.Clients'

Es ist nicht ganz klar aus Ihrer Frage, aber andere Optionen für grepumfassen:

  • Die -iFlagge fallen lassen.
  • Verwenden des -FFlags für eine feste Zeichenfolge
  • Deaktivieren von NLS mit LANG=C
  • Festlegen einer maximalen Anzahl von Übereinstimmungen mit dem -mFlag.
Steve
quelle
2
Wenn es sich um eine tatsächliche Datei handelt, verwenden Sie --pipepartanstelle von --pipe. Es ist viel schneller.
Ole Tange
Diese Verwendung unterstützt kein Muster, einschließlich Speicherplatz. Wir müssen Folgendes verwenden: parallel --pipe --block 10M "/ usr / bin / grep -F -C5 -e 'Animal Care & Pets'"
zw963
Was bedeutet das <Zeichen vor dem Parallelbefehl?
Elcortegano
1
@elcortegano: Das nennt man E / A-Umleitung . Grundsätzlich liest es Eingaben aus dem folgenden Dateinamen. Ähnlich wie cat file.sql | parallel ...ein UUOC , vermeidet ihn jedoch . GNU parallel bietet auch die Möglichkeit, Eingaben aus einer Datei mit zu lesen parallel ... :::: file.sql. HTH.
Steve
10

Einige triviale Verbesserungen:

  • Entfernen Sie die Option -i, wenn Sie können. Die Groß- und Kleinschreibung wird nicht berücksichtigt.

  • Ersetzen Sie die .durch\.

    Ein einzelner Punkt ist das Regex-Symbol für jedes Zeichen, das ebenfalls langsam ist

BeniBela
quelle
3

Zwei Angriffslinien:

  • Bist du sicher, dass du das brauchst -ioder hast du eine Möglichkeit, es loszuwerden?
  • Hast du mehr Kerne zum Spielen? grepist Single-Threaded, daher möchten Sie möglicherweise mehr davon an verschiedenen Offsets starten.
Eugen Rieck
quelle
1
< eightygigsfile.sql parallel -k -j120% -n10 -m grep -F -i -C 5 'db_pd.Clients'  

Wenn Sie nach mehreren Zeichenfolgen suchen müssen, spart grep -f strings.txt eine Menge Zeit. Das Obige ist eine Übersetzung von etwas, das ich gerade teste. Der Optionswert -j und -n schien für meinen Anwendungsfall am besten zu funktionieren. Das -F grep machte auch einen großen Unterschied.

user584583
quelle