Grepping un file enorme (80 GB) in qualche modo per accelerarlo?


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

Questo è in esecuzione da un'ora su un server Linux abbastanza potente che altrimenti non sarebbe sovraccarico. Qualche alternativa a grep? Qualcosa sulla mia sintassi che può essere migliorata, (egrep, fgrep meglio?)

Il file si trova effettivamente in una directory condivisa con un montaggio su un altro server ma lo spazio su disco effettivo è locale, quindi non dovrebbe fare alcuna differenza?

il grep sta prendendo fino al 93% di CPU


8
A seconda delle impostazioni locali, lo -iswitch potrebbe rallentare il processo, provare senza -io con LC_ALL=C grep .... Inoltre, se stai solo cercando una stringa fissa, usa grep -F.
Thor

5
Come menzionato da @dogbane, l'utilizzo della variabile LC_ALL = C insieme a fgrep può accelerare la ricerca.Ho fatto alcuni test e sono stato in grado di ottenere un aumento delle prestazioni del 1400% e ho scritto un articolo dettagliato sul motivo per cui questo è nel mio post di accelerazione grep
JacobN

Sono curioso: quale file ha una dimensione di 80 GB? Mi piace pensare che quando un file diventa così grande, potrebbe esserci una migliore strategia di archiviazione (ad esempio ruotare i file di registro o categorizzare gerarchicamente in diversi file e cartelle). Inoltre, se le modifiche si verificano solo in alcuni punti del file (es. Alla fine), memorizza semplicemente alcuni risultati grep dalla sezione precedente che non cambiano e invece di grep il file originale, grep il file dei risultati memorizzato.
Sridhar Sarnobat

Ho optato per github.com/google/codesearch : sia l'indicizzazione che la ricerca sono velocissime (scritte in Go). cindex .per indicizzare la cartella corrente, quindi csearch db_pd.Clients.
ccpizza

1
Se il tuo file fosse indicizzato o ordinato, questo potrebbe essere reso molto più veloce. La ricerca di ogni riga è O (n) per definizione, mentre un file ordinato può essere cercato dividendolo in due - a quel punto parleresti meno di un secondo per cercare i tuoi 80 GB (quindi perché un database indicizzato da 80 GB non richiede tempo per un semplice SELECT, mentre il tuo grep impiega ... beh, tutto il tempo che ci vuole).
Charles Duffy

Risposte:


148

Ecco alcune opzioni:

1) Prefissa il tuo comando grep con LC_ALL=Cper usare la localizzazione C invece di UTF-8.

2) Usa fgrepperché stai cercando una stringa fissa, non un'espressione regolare.

3) Rimuovi l' -iopzione, se non ti serve.

Quindi il tuo comando diventa:

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

Sarà anche più veloce se copi il tuo file sul disco RAM.


5
è stato MOLTO più veloce di un ordine di grandezza, grazie. BTW ho aggiunto -n per ottenere i numeri di riga. Forse anche un -m per uscire dopo la partita
zzapper

5
Wow, grazie mille @dogbane ottimo suggerimento! Questo mi ha portato in un tunnel di ricerca per scoprire perché LC_ALL = C accelera grep ed è stata un'esperienza molto illuminante!
JacobN

7
Ad alcune persone (non io) piace grep -Fpiù difgrep
Walter Tross

2
La mia comprensione è che LANG=C(invece di LC_ALL=C) è sufficiente ed è più facile da digitare.
Walter Tross

2
@Adrian fgrepè un altro modo di scrivere grep -F, come man fgrepti dirò. Alcune versioni mandicono anche che la prima è deprecata per la seconda, ma la forma più breve è troppo comoda per morire.
Walter Tross

36

Se hai una CPU multicore, consiglierei davvero GNU parallel . Per eseguire il grep di un file di grandi dimensioni in parallelo, utilizzare:

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

A seconda dei dischi e delle CPU, potrebbe essere più veloce leggere blocchi più grandi:

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

Non è del tutto chiaro dalla tua domanda, ma altre opzioni per grepincludono:

  • Far cadere la -ibandiera.
  • Utilizzo del -Fflag per una stringa fissa
  • Disabilitare NLS con LANG=C
  • Impostazione di un numero massimo di corrispondenze con la -mbandiera.

2
Se si tratta di un file effettivo, utilizza --pipepartinvece di --pipe. È molto più veloce.
Ole Tange

Questo modello di utilizzo non supporta lo spazio, è necessario utilizzarlo in questo modo: parallel --pipe --block 10M "/ usr / bin / grep -F -C5 -e 'Animal Care & Pets'"
zw963

Cosa significa il <carattere che precede il comando parallelo?
elcortegano

1
@elcortegano: Questo è quello che viene chiamato redirezione I / O . Fondamentalmente, legge l'input dal seguente nome di file. Simile a cat file.sql | parallel ...ma evita un UUOC . GNU parallel ha anche un modo per leggere l'input da un file usando parallel ... :::: file.sql. HTH.
Steve

10

Qualche miglioramento banale:

  • Rimuovi l'opzione -i, se puoi, la distinzione tra maiuscole e minuscole è piuttosto lenta.

  • Sostituisci .da\.

    Un singolo punto è il simbolo regex per abbinare qualsiasi carattere, che è anche lento


3

Due linee di attacco:

  • sei sicuro di aver bisogno del -i, o hai la possibilità di sbarazzartene?
  • Hai più core con cui giocare? grepè a thread singolo, quindi potresti voler avviare più di essi con offset diversi.

1
< eightygigsfile.sql parallel -k -j120% -n10 -m grep -F -i -C 5 'db_pd.Clients'  

Se è necessario cercare più stringhe, grep -f strings.txt consente di risparmiare un sacco di tempo. Quanto sopra è una traduzione di qualcosa che sto attualmente testando. il valore dell'opzione -j e -n sembrava funzionare meglio per il mio caso d'uso. Anche il -F grep ha fatto una grande differenza.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.