Grepping d'un énorme fichier (80 Go) pour l'accélérer?

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

Cela fonctionne depuis une heure sur un serveur Linux assez puissant qui n'est autrement pas surchargé. Une alternative à grep? Quelque chose à propos de ma syntaxe qui peut être amélioré, (egrep, fgrep mieux?)

Le fichier est en fait dans un répertoire qui est partagé avec un montage sur un autre serveur mais l'espace disque réel est local, donc cela ne devrait pas faire de différence?

le grep saisit jusqu'à 93% du CPU

zzapper
la source
8
Selon vos paramètres régionaux, le -icommutateur peut ralentir le processus, essayez sans -iou avec LC_ALL=C grep .... De plus, si vous recherchez uniquement une chaîne fixe, utilisez grep -F.
Thor
5
Comme mentionné @dogbane en utilisant la LC_ALL = C variable le long avec fgrep peut accélérer votre search.I a fait quelques tests et a été en mesure d'atteindre un 1400% augmentation de la performance et a écrit un article détaillé pourquoi cela est dans ma vitesse jusqu'à grep post
JacobN
Je suis curieux - quel fichier fait 80 Go? J'aimerais penser que lorsqu'un fichier devient aussi gros, il peut y avoir une meilleure stratégie de stockage (par exemple, la rotation des fichiers journaux ou la catégorisation hiérarchique dans différents fichiers et dossiers). De plus, si les changements ne se produisent qu'à certains endroits du fichier (par exemple à la fin), stockez simplement quelques résultats de grep de la section précédente qui ne changent pas et au lieu de grepping le fichier d'origine, grep le fichier de résultat stocké.
Sridhar Sarnobat
Je me suis installé sur github.com/google/codesearch - l'indexation et la recherche sont rapides comme l'éclair (écrites en Go). cindex .pour indexer votre dossier actuel, puis csearch db_pd.Clients.
ccpizza
1
Si votre fichier était indexé ou trié, cela pourrait être beaucoup plus rapide. La recherche de chaque ligne est O (n) par définition, alors qu'un fichier trié peut être recherché en le divisant en deux - à quel point vous parleriez en moins d'une seconde pour rechercher vos 80 Go (d'où pourquoi une base de données indexée de 80 Go ne prend pas du tout de temps pour un simple SELECT, alors que votre grep prend ... enfin, aussi longtemps qu'il en faut).
Charles Duffy

Réponses:

148

Voici quelques options:

1) Préfixez votre commande grep avec LC_ALL=Cpour utiliser la locale C au lieu de UTF-8.

2) À utiliser fgrepparce que vous recherchez une chaîne fixe, pas une expression régulière.

3) Supprimez l' -ioption si vous n'en avez pas besoin.

Votre commande devient donc:

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

Ce sera également plus rapide si vous copiez votre fichier sur le disque RAM.

dogbane
la source
5
c'était BEAUCOUP plus rapide d'un ordre de grandeur merci. BTW j'ai ajouté -n pour obtenir les numéros de ligne. Peut-être aussi un -m pour quitter après le match
zzapper
5
Wow merci beaucoup @dogbane super conseil! Cela m'a conduit dans un tunnel de recherche pour découvrir pourquoi LC_ALL = C accélère grep et ce fut une expérience très enrichissante!
JacobN
7
Certaines personnes (pas moi) aiment grep -Fplus quefgrep
Walter Tross
2
Je crois comprendre que LANG=C(au lieu de LC_ALL=C) est suffisant et qu'il est plus facile à taper.
Walter Tross
2
@Adrian fgrepest une autre façon d'écrire grep -F, comme man fgrepvous le direz. Certaines versions du mandit également que le premier est déconseillé pour le second, mais que la forme plus courte est trop pratique pour mourir.
Walter Tross
36

Si vous avez un processeur multicœur, je recommanderais vraiment GNU parallèle . Pour grep un gros fichier en utilisation parallèle:

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

En fonction de vos disques et de vos processeurs, il peut être plus rapide de lire des blocs plus volumineux:

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

Ce n'est pas tout à fait clair d'après votre question, mais d'autres options grepincluent:

  • Laisser tomber le -idrapeau.
  • Utilisation de l' -Findicateur pour une chaîne fixe
  • Désactiver NLS avec LANG=C
  • Définition d'un nombre maximum de correspondances avec le -mdrapeau.
Steve
la source
2
S'il s'agit d'un fichier réel, utilisez à la --pipepartplace de --pipe. C'est beaucoup plus rapide.
Ole Tange
Cette utilisation ne prend pas en charge le modèle d'espace, nous devons l'utiliser comme ceci: parallel --pipe --block 10M "/ usr / bin / grep -F -C5 -e 'Animal Care & Pets'"
zw963
Que signifie le <caractère précédant la commande parallèle?
elcortegano
1
@elcortegano: C'est ce qu'on appelle la redirection d' E / S . Fondamentalement, il lit l'entrée du nom de fichier suivant. Similaire à cat file.sql | parallel ...mais évite un UUOC . GNU parallel a également un moyen de lire l'entrée d'un fichier en utilisant parallel ... :::: file.sql. HTH.
Steve
10

Quelques améliorations insignifiantes:

  • Supprimez l'option -i, si vous le pouvez, insensible à la casse est assez lent.

  • Remplacez le .par\.

    Un seul point est le symbole regex pour correspondre à n'importe quel caractère, qui est également lent

BeniBela
la source
3

Deux lignes d'attaque:

  • êtes-vous sûr, vous avez besoin du -i, ou avez-vous la possibilité de vous en débarrasser?
  • Avez-vous plus de cœurs avec lesquels jouer? grepest monothread, vous voudrez peut-être en commencer plus avec des décalages différents.
Eugen Rieck
la source
1
< eightygigsfile.sql parallel -k -j120% -n10 -m grep -F -i -C 5 'db_pd.Clients'  

Si vous avez besoin de rechercher plusieurs chaînes, grep -f strings.txt vous fait gagner beaucoup de temps. Ce qui précède est une traduction de quelque chose que je teste actuellement. la valeur des options -j et -n semblait fonctionner le mieux pour mon cas d'utilisation. Le grep -F a également fait une grande différence.

user584583
la source