grep 검색을 기반으로 스크립트 속도를 높이는 방법은 무엇입니까?

grep 검색을 기반으로 스크립트 속도를 높이는 방법은 무엇입니까?

두 개의 쉼표로 구분된 값이 포함된 매우 큰 텍스트 파일이 있습니다.

78414387,10033
78769989,12668
78771319,13677
78771340,13759
80367563,16336
81634533,10025
82878571,10196
110059366,10218
110059411,10812
110059451,10067

다음과 같이 로그 파일에서 해당 값을 검색해야 합니다.

- delivery-AMC_prod_product 231825855936862016-07-02 00:00:52 c.c.i.d.s.d.DeliveryTopologyFactory$$anon$1$$anon$2 [INFO] ack: uid=57773c737e3d80d7def296c7| id=278832702| version=28| timestamp=1467432051000
- delivery-AMC_prod_product 231825855936862016-07-02 00:00:52 c.c.i.d.s.d.DeliveryTopologyFactory$$anon$1$$anon$2 [INFO] ack: uid=57773c732f18c26fe604fd04| id=284057302| version=9| timestamp=1467432051000
- delivery-AMC_prod_product 231825855936862016-07-02 00:00:52 c.c.i.d.s.d.DeliveryTopologyFactory$$anon$1$$anon$2 [INFO] ack: uid=57773c747e3d80d7def296c8| id=357229| version=1151| timestamp=1467432052000
- delivery-AMC_prod_product 231825855936862016-07-02 00:00:52 c.c.i.d.s.d.DeliveryTopologyFactory$$anon$1$$anon$2 [INFO] ack: uid=57773c742f18c26fe604fd05| id=279832706| version=35| timestamp=1467432052000
- delivery-AMC_prod_product 231825855936862016-07-02 00:00:52 c.c.i.d.s.d.DeliveryTopologyFactory$$anon$1$$anon$2 [INFO] ack: uid=57773c744697ddb976cf5a95| id=354171| version=503| timestamp=1467432052000
- delivery-AMC_prod_product 231825855936862016-07-02 00:00:53 c.c.i.d.s.d.DeliveryTopologyFactory$$anon$1$$anon$2 [INFO] ack: uid=57773c754697ddb976cf5a96| id=355638| version=1287| timestamp=1467432053000

내 스크립트:

#!/bin/bash
COUNT=0
while IFS=',' read ID VERSION; do
    VERSION=`echo $VERSION |col -bx`
    if (grep "id=${ID}| version=$VERSION" worker-6715.log.2016-$1.log.* > /dev/null); then
            let "COUNT++"
    else
            echo "$ID, $VERSION FAIL"
            exit 2

    fi
done < recon.txt

echo "All OK, $COUNT checked"
  • 로그 파일에서 불필요한 필드를 제거하면 실행 속도가 빨라지나요?
  • RAM 장치를 생성하고 거기에 로그 파일을 복사하면 실행 속도가 빨라지나요?레드햇 리눅스 6(Hedwig) 파일을 캐시하는 방법은 무엇입니까? 다른 제안이 있나요?

답변1

병목 현상은 recon.txt셸에서 파일을 한 줄씩 읽는 중입니다. 실패한 행을 얻으려면 로그의 행을 전처리하여 행과 비슷하게 보이도록 한 recon.txt다음 이를 사용하여 comm(1)다음과 같이 설정의 차이점을 찾을 수 있습니다.

comm -23 \
    <(sort -u recon.txt) \
    <(sed 's/.*| id=\([0-9]*\)| version=\([0-9]*\)|.*/\1,\2/' worker-6715.log.2016-$1.log.* | \
        sort -u)

이는 구성을 처리할 수 있는 쉘이 있다고 가정합니다 <(...). 또한 결과의 행 순서는 유지되지 않습니다 recon.txt. 이 순서를 유지하는 것이 더 어렵고 느릴 것입니다.

성공 횟수도 필요한 경우 반대 작업을 수행하여 recon.txt로그에서 찾은 것과 비슷하게 보이도록 전처리한 다음 fgrep(1)또는 grep -F를 사용하여 검색할 수 있습니다 . 로케일을 다음으로 설정하면 C일부 시스템에서 작업 속도가 크게 향상될 수도 있습니다. 그러므로:

COUNT=$( \
    sed 's/\([0-9]*\),\([0-9]*\)/| id=\1| version=\2|/' recon.txt | \
    LC_ALL=C fgrep -f - worker-6715.log.2016-$1.log.* | \
    wc -l )

이는 recon.txt중복 항목이 포함되지 않고 각 행이 recon.txt모든 로그에서 최대 한 번 일치한다고 가정합니다. 첫 번째 제한을 해제하는 것은 어려울 것입니다. 두 번째는 신중한 선택을 통해 들어올릴 수 있습니다 comm(1).

답변2

지적했듯이 여기서 주요 문제는 쉘 루프입니다. 먼저 awk로그 파일, 연결 idversion값을 처리하고 결과를 배열에 저장한 다음 읽고 recon.txt각 줄에서 배열에 있는지 확인합니다. 그렇지 않은 경우 줄 내용을 변수에 저장하고 t즉시 exit( END블록 실행 ). 블록에 END메시지와 함께 저장된 행이 있으면 그렇지 않으면 OK 메시지만 인쇄하고 아무 것도 인쇄하지 않습니다. 행 수:texit 2recon.txt

awk 'NR==FNR{j=$9","$10;gsub(/[^0-9,]/, "", j);a[j]++;next}
!($0 in a){t=$0;{exit}}
END{if (t){print t, "FAIL"; exit 2}
else{print "All OK,", FNR, "checked"}}' logfile recon.txt

id이는 및 값이 version각각 필드 9와 10에 있다고 가정합니다. 그렇지 않은 경우 다음과 같은 정규식을 사용할 수 있습니다.사토실제로 - 즉, awk스타일이 역참조를 지원하는 경우 그렇지 않으면 다음과 같은 일이 발생합니다.

NR==FNR{sub(/.* id=/, "");sub(/\| version=/, ",");sub(/\|.*/, ""));a[$0]++;next}

답변3

나는 이 파일들을 데이터베이스(원하는 모든 유형)에 넣습니다. 아마도 큰 파일을 가져오지 않고 해당 출력을 생성한 응용 프로그램에서 한 줄씩 입력할 수도 있습니다.

이렇게 하면 DB 엔진은 매번 grep을 호출하여 리소스를 낭비하지 않고 대신 C로 컴파일된 내부 함수를 사용합니다.

가져오기(전체 파일을 가져오는 경우) 또는 테이블을 생성한 후(행별로 레코드를 삽입하는 경우) 색인을 생성하는 것을 잊지 마십시오.

답변4

먼저 recon.txt를 grep -F가 빠르게 사용할 수 있는 형식으로 변환합니다.

perl -pe 's/,/| version=/' recon.txt > recon.grep-F

그렇다면 당신의 상황은 위에서 설명한 것과 같습니다https://www.gnu.org/software/parallel/parallel_examples.html#example-grepping-n-lines-for-m-regular-expressions

귀하의 질문에 맞는 텍스트는 다음과 같습니다.

정규식이 많은 대용량 파일을 grep하는 가장 간단한 솔루션은 다음과 같습니다.

grep -f regexps.txt bigfile

또는 정규식이 고정 문자열인 경우:

grep -F -f recon.grep-F worker.log

CPU와 디스크 I/O라는 두 가지 제한 요소가 있습니다. CPU는 측정하기 쉽습니다. grep이 CPU의 90%를 초과하는 경우(예: top 실행 시) CPU가 제한 요소이므로 병렬화가 속도를 높입니다. 그렇지 않은 경우 디스크 I/O가 제한 요소이며 디스크 시스템에 따라 병렬화가 더 빨라질 수도 있고 느려질 수도 있습니다. 확실히 알 수 있는 유일한 방법은 측정하는 것입니다.

CPU가 제한 요소인 경우 정규식을 병렬화해야 합니다.

cat recon.grep-F | parallel --pipe -L1000 --round-robin grep -F -f - worker.log

두 개 이상의 정규 표현식과 일치하면 한 줄이 중복될 수 있습니다. 이 명령은 CPU당 grep을 시작하고 CPU당 한 번 큰 파일을 읽습니다. 그러나 이 작업은 병렬로 수행되므로 첫 번째 읽기를 제외한 모든 읽기가 RAM에 캐시됩니다. regexp.txt의 크기에 따라 -L1000 대신 --block 10m을 사용하는 것이 더 빠를 수도 있습니다. regexp.txt가 너무 커서 RAM에 맞지 않으면 --round-robin을 제거하고 -L1000을 조정하십시오. 이렇게 하면 빅파일을 더 많이 읽을 수 있습니다.

일부 스토리지 시스템은 여러 블록을 병렬로 읽을 때 성능이 더 좋습니다. 이는 일부 RAID 시스템 및 일부 네트워크 파일 시스템에 해당됩니다. 대용량 파일을 병렬로 읽기:

parallel --pipepart --block 100M -a worker.log -k grep -F -f recon.grep-F

그러면 빅파일이 100MB 청크로 분할되고 각 청크에 대해 grep이 실행됩니다. bigfile과 regexp.txt를 병렬로 읽으려면 --fifo를 사용하여 두 가지를 결합하십시오.

parallel --pipepart --block 100M -a worker.log --fifo cat recon.grep-F \
\| parallel --pipe -L1000 --round-robin grep -f - {}

두 개 이상의 정규 표현식과 일치하면 한 줄이 중복될 수 있습니다.

관련 정보