2 votos

El método más rápido para filtrar un archivo enorme

Tengo un archivo de syslog con 4 gb de tamaño a cada minuto, y a continuación es de 2 líneas de 4.5 millones de línea en cada minuto,

y quiero generar un archivo nuevo con sólo unas pocas columnas eventtime|srcip|dstip, por lo que el resultado será el siguiente

1548531299|X.X.X.X|X.X.X.X

por favor, tenga en cuenta que la posición de los campos es aleatorio.
He probado algunos filtros pero sigue consumiendo más de 40 minutos, para controlar un archivo en un poderoso VM máquina con 4 Núcleos y 16 gb de ram.

Por lo que hay un método para manejar tales archivo muy grande y filtro de la columna requerida en un método rápido ?

{Jan 26 22:35:00 172.20.23.148 date=2019-01-26 time=22:34:59 devname="ERB-03" devid="5KDTB18800169" logid="0000000011" type="traffic" subtype="forward" level="warning" vd="Users" eventtime=1548531299 srcip=X.X.X.X srcport=3XXXX srcintf="GGI-cer.405" srcintfrole="undefined" dstip=X.X.X.X dstport=XX dstintf="hh-BB.100" dstintfrole="undefined" sessionid=xxxxxxx proto=6 action="ip" policyid=5 policytype="policy" service="HTTP" appcat="unscanned" crscore=5 craction=xxxxxx crlevel="low"

Jan 26 22:35:00 172.20.23.148 date=2019-01-26 time=22:34:59 devname="XXX-XX-FGT-03" devid="XX-XXXXXXXX" logid="0000000013" type="traffic" subtype="forward" level="notice" vd="Users" eventtime=1548531299 srcip=X.X.X.X srcport=XXXXX srcintf="XXX-Core.123" srcintfrole="undefined" dstip=X.X.X.X dstport=XX dstintf="sXX-CC.100" dstintfrole="undefined" sessionid=1234567 cvdpkt=0 appcat="unscanned" crscore=5 craction=123456 crlevel="low"}

3voto

Mike Puntos 1

Perl para el rescate

Guardar la secuencia de comandos siguiente como filter.pl y hacerlo ejecutable (chmod +x):

#!/usr/bin/env perl

use strict;
use warnings;

while( <> ) {
    if ( /^(?=.*eventtime=(\S+))(?=.*srcip=(\S+))(?=.*dstip=(\S+)).*$/ ) {
        print "$1|$2|$3\n";
    }
}

A continuación, ejecute

pduck@ubuntu:~> time ./filter.pl < input.txt > output.txt

real    0m44,984s
user    0m43,965s
sys     0m0,973s

La expresión regular que utiliza un lookaround patrón, en este caso una búsqueda positiva hacia delante, para que coincida con los tres valores de eventtime, srcipy dstip en cualquier orden.

Me duplican sus dos líneas de entrada hasta que llegué a un archivo de 4 GB y aproximadamente 9 millones de líneas. Me encontré con el código en un SSD.

2voto

Rufo Puntos 121

Me duplican sus dos 'de entrada' líneas a un tamaño de archivo 3867148288 bytes (3.7 GiB) y pude proceso de con grep en 8 minutos y 24 segundos (leer y escribir en un disco duro. Debería ser más rápido el uso de un SSD o ramdrive).

Con el fin de minimizar el tiempo utilizado, he utilizado sólo las características estándar de grep, y no el post-proceso, de modo que el formato de salida no es lo que usted especifique, pero podría ser útil de todos modos. Usted puede probar este comando

time grep -oE -e 'eventtime=[0-9]* ' \
 -e 'srcip=[[:alnum:]]\.[[:alnum:]]\.[[:alnum:]]\.[[:alnum:]]' \
 -e 'dstip=[[:alnum:]]\.[[:alnum:]]\.[[:alnum:]]\.[[:alnum:]]' \
infile > outfile

La salida de sus dos líneas:

$ cat outfile
eventtime=1548531298 
srcip=X.X.X.Y
dstip=X.X.X.X
eventtime=1548531299 
srcip=X.X.X.Z
dstip=X.X.X.Y

El archivo de salida contiene 25165824 líneas correspondientes a 8388608 (8,3 millones) líneas en el archivo de entrada.

$ wc -l outfile
25165824 outfile
$ <<< '25165824/3' bc
8388608

Mi prueba indica que grep puede procesar alrededor de 1 millón de líneas por minuto.

A menos que su ordenador es mucho más rápido que el mío. esto no es lo suficientemente rápido, y creo que usted tiene que considerar algo que es varias veces más rápido, probablemente filtrado antes de escribir el archivo de registro, pero lo mejor sería evitar completamente la salida de lo que no es necesario (y evitar el filtrado).


El archivo de entrada se realiza por medio de la duplicación, y tal vez el sistema "recuerda" que ha visto en la misma línea antes y hace las cosas más rápido, así que no sé cómo de rápido va a trabajar con un archivo con todos los imprevistos y las variaciones. Usted tiene que probarlo.


Edit1: me encontré con la misma tarea en un Dell M4800 con un procesador Intel de 4ª generación del procesador i7 y un disco SSD. Se terminó en 4 minutos y 36 segundos, a casi el doble de velocidad, 1.82 millones de líneas por minuto.

$ <<< 'scale=2;25165824/3/(4*60+36)*60/10^6' bc
1.82

Sigue siendo demasiado lento.


Edit2: he simplificado la grep patrones y corrió otra vez en la de Dell.

time grep -oE -e 'eventtime=[^\ ]*' \
 -e 'srcip=[^\ ]*' \
 -e 'dstip=[^\ ]*' \
infile > out

Terminó después de 4 minutos y 11 segundos, una pequeña mejora para 2.00 millones de líneas por minuto

$ <<< 'scale=2;25165824/3/(4*60+11)*60/10^6' bc
2.00

Edit 3: @JJoao, extensión de perl acelera grep a 39 segundos correspondiente a 12.90 millones de líneas por minuto en el equipo, donde lo ordinario grep lee de 1 millón de líneas por minuto (leer y escribir en una unidad de disco duro).

$ time grep -oP '\b(eventtime|srcip|dstip)=\K\S+' infile >out-grep-JJoao

real    0m38,699s
user    0m31,466s
sys     0m2,843s

Esta extensión de perl es experiental según info grep pero funciona en mi Lubuntu 18.04.1 LTS.

'-P' '--perl-regexp' Interpretar el patrón como un Perl compatible regular la expresión (PCRE). Esto es experimental, especialmente cuando se combina con la '-z' ('--null-data') y 'grep -P' puede advertir de no implementadas características. *Nota: Otras Opciones::.

También me compila un programa en C de acuerdo con @JJoao s flex método, y finshed en 53 segundos correspondiente a 9.49 millones de líneas por minuto en el equipo, donde lo ordinario grep lee de 1 millón de líneas por minuto (leer y escribir en una unidad de disco duro). Ambos métodos son rápidos, pero grep con la extensión de perl es la más rápida.

$ time ./filt.jjouo < infile > out-flex-JJoao

real    0m53,440s
user    0m48,789s
sys 0m3,104s

Edición 3.1: En el Dell M4800 con un SSD he tenido los siguientes resultados,

time ./filt.jjouo < infile > out-flex-JJoao

real    0m25,611s
user    0m24,794s
sys 0m0,592s

time grep -oP '\b(eventtime|srcip|dstip)=\K\S+' infile >out-grep-JJoao

real    0m18,375s
user    0m17,654s
sys 0m0,500s

Esto corresponde a

  • 19.66 millones de líneas por minuto para el flex aplicación
  • 27.35 millones de líneas por minuto para grep con la extensión de perl

Editar 3.2: En el Dell M4800 con un SSD he tenido los siguientes resultados, cuando he usado la opción -f a la flexión de preprocesador,

flex -f -o filt.c filt.flex
cc -O2 -o filt.jjoao filt.c

El resultado fue una mejora, y ahora la flex aplicación muestra la velocidad máxima

flex -f ...

$ time ./filt.jjoao < infile > out-flex-JJoao

real    0m15,952s
user    0m15,318s
sys 0m0,628s

Esto corresponde a

  • 31.55 millones de líneas por minuto para el flex de aplicación.

2voto

JJoao Puntos 393

Si quieres una solución realmente rápida te sugiero la herramienta flex. Flex genera C. Lo siguiente es capaz de procesar ejemplos como el que se presenta y acepta campos de orden libre. Entonces cree un archivo, denominado f.fl con el siguiente contenido:

 %option main
%%
  char e[100], s[100], d[100];

eventtime=[^ \n]*   { strcpy(e,yytext+10); }
srcip=[^ \n]*       { strcpy(s,yytext+6);  }
dstip=[^ \n]*       { strcpy(d,yytext+6);  }
\n                  { if (e[0] && s[0] && d[0] )printf("%s|%s|%s\n",e,s,d); 
                      e[0]=s[0]=d[0]=0 ;}
.                   {}
%%
 

Para probar intente:

 $ flex -f -o f.c f.fl 
$ cc -O2 -o f f.c
$ ./f < input > output
 

Aquí está la comparación time :

 $ time ./f < 13.5-milion-lines-3.9G-in-file > out-file
real    0m35.689s
user    0m34.705s
sys     0m0.908s
 

1voto

Spas Spasov Puntos 305

He aquí una posible solución, a partir de esta respuesta, proporcionada por @PerlDuck hace un tiempo:

#!/bin/bash
while IFS= read -r LINE
do
    if [[ ! -z ${LINE} ]]
    then
        eval $(echo "$LINE" | sed -e 's/\({\|}\)//g' -e 's/ /\n/g' | sed -ne '/=/p')
        echo "$eventtime|$srcip|$dstip"
    fi
done < "$1"

No sé cómo se comportará en tales archivo de gran tamaño, la OMI un awk solución será mucho más rápido. Aquí es cómo funciona con el archivo de entrada ejemplo:

$ ./script.sh in-file
1548531299|X.X.X.X|X.X.X.X
1548531299|X.X.X.X|X.X.X.X

Aquí es el resultado de una productividad time prueba, realizada de forma regular i7, equipado con una unidad SSD y 16GB de RAM:

$ time ./script.sh 160000-lines-in-file > out-file

real    4m49.620s
user    6m15.875s
sys     1m50.254s

EnMiMaquinaFunciona.com

EnMiMaquinaFunciona es una comunidad de administradores de sistemas en la que puedes resolver tus problemas y dudas.
Puedes consultar las preguntas de otros sysadmin, hacer tus propias preguntas o resolver las de los demás.

Powered by: