병렬 스크립트 프로세스

병렬 스크립트 프로세스

IP별로 Apache 액세스 로그를 구문 분석하고 싶습니다. 다음 코드를 사용했는데 거의 90초가 걸렸습니다.

grep "^$CLIENT_IP" /var/log/http/access.log > /tmp/access-$CLIENT_IP.log

그런 다음 다음 대안을 시도했습니다.

sed -i -e "/^$CLIENT_IP/w /tmp/access-$CLIENT_IP.log" -e '//d' /var/log/http/access.log

심지어 이 작업도 60초 이상 걸렸습니다.

해결해야 할 IP는 1200개입니다. 실행 시간을 줄이기 위해 병렬성을 달성할 수 있는 방법이 있는지 알고 싶습니다.

답변1

아마도 텍스트 파일의 IP 주소를 사용하여 모든 IP 주소에 대한 쉘 루프에서 이 작업을 수행한다고 가정합니다. 예, IP 주소 sed당 한 번씩 호출하면 grep속도가 느려집니다.

sed대신 잘 준비하면 한 번쯤은 운이 따를 수도 있다.

먼저 스크립트를 만들어야 합니다. sedIP 주소가 한 줄에 하나씩 포함된 파일에서 이 작업을 수행합니다.ip.list

sed -e 'h' \
    -e 's/\./\\./g' \
    -e 's#.*#/^&[[:blank:]]/w /tmp/access-#' \
    -e 'G' \
    -e 's/\n//' \
    -e 's/$/.log/' ip.list >ip.sed

이것은 sed각 IP 주소에 대해

  1. 주소를 "예약된 공간"( 의 추가 버퍼 sed)에 복사합니다.
  2. ."패턴 공간"(입력 줄)을 \.(포인트를 올바르게 일치시키기 위해 코드에서는 이 작업을 수행하지 않음)로 변경합니다.
  3. 패턴 공간 앞에 추가 ^합니다 .[[:blank:]]/w /tmp/access-
  4. 개행 문자를 사이에 두고 예약된 공간에서 패턴 공간으로 수정되지 않은 입력 줄을 추가합니다.
  5. 개행 문자를 제거합니다.
  6. .log줄의 끝에 추가 하고 결과를 암시적으로 인쇄합니다.

다음을 포함하는 파일의 경우

127.0.0.1
10.0.0.1
10.0.0.100

sed그러면 스크립트가 생성됩니다.

/^127\.0\.0\.1[[:blank:]]/w /tmp/access-127.0.0.1.log
/^10\.0\.0\.1[[:blank:]]/w /tmp/access-10.0.0.1.log
/^10\.0\.0\.100[[:blank:]]/w /tmp/access-10.0.0.100.log

IP 주소 뒤에 공백 문자(공백 또는 탭)가 일치해야 합니다. 그렇지 않으면 로그 항목이 파일 10.0.0.100에 저장됩니다 /tmp/access-10.0.0.1.log. 귀하의 코드는 이를 무시합니다.

그런 다음 반복 없이 로그 파일에서 사용할 수 있습니다.

sed -n -f ip.sed /var/log/http/access.log

나는 동일한 스크립트에서 sed1200개의 파일을 작성하는 것을 테스트한 적이 없습니다. 작동하지 않으면 다음 awk변형을 시도해 보십시오.


비슷한 해결 방법은 awk먼저 IP 주소를 배열로 읽어온 다음 이를 각 행과 일치시키는 것입니다. 이를 위해서는 한 번의 awk호출이 필요합니다.

awk 'FNR == NR  { list[$1] = 1; next }
     $1 in list { name = $1 ".log"; print >>name; close name }' ip.list /var/log/http/access.log

여기서는 awkIP 목록과 로그 파일을 모두 제공합니다. NR == FNR아직 첫 번째 파일(목록)을 읽고 있다는 것을 알게 되면 IP list번호를 연관 배열의 키로 추가하고 다음 입력 줄을 계속 진행합니다.

조건이 충족되지 않으면 FNR == NR두 번째 파일(로그 파일)에서 읽고 입력 줄의 첫 번째 필드가 키 필드인지 테스트합니다 list(이것은 정규식 일치가 아닌 순수 문자열 비교입니다). 그렇다면 적절한 이름의 파일에 행을 추가합니다.

출력 파일을 닫을 때 주의해야 합니다. 그렇지 않으면 열려 있는 파일 설명자가 부족해질 수 있습니다. 따라서 추가하기 위해 열고 닫는 파일이 많이 있지만 awkIP 주소당 한 번 호출(또는 해당 문제에 대한 유틸리티)하는 것보다 여전히 빠릅니다.


이것이 귀하에게 효과가 있는지, 그리고 대략적인 런타임이 무엇인지 알고 싶습니다. 저는 매우 작은 데이터 세트에서만 이러한 솔루션을 테스트했습니다.


물론 우리는 귀하의 아이디어에 동의하고 grep시스템에 여러 인스턴스를 병렬로 던져서 이를 무차별 대입할 수 있습니다.

IP 주소의 점들이 정확하게 일치하지 않았다는 사실을 무시하고, 아마도

xargs -P 4 -n 100 sh -c '
    for n do
        grep "^$n[[:blank:]]" /var/log/http/access.log >"/tmp/access-$n.log"
    done' sh <ip.list

여기서는 짧은 쉘 스크립트에 xargs한 번에 최대 100개의 IP 주소가 파일에서 제공됩니다. ip.list4개의 병렬 스크립트 호출을 예약합니다.

짧은 쉘 스크립트:

for n do
    grep "^$n[[:blank:]]" /var/log/http/access.log >"/tmp/access-$n.log"
done

xargs이는 명령줄에 제공된 100개의 IP 주소를 반복하고 grep4개의 루프가 병렬로 실행된다는 점을 제외하면 사용한 것과 거의 동일한 명령을 적용합니다.

보유한 CPU 수를 늘리 -P 4거나 이와 관련됩니다. 각 병렬 인스턴스가 동일한 디스크에서 읽고 쓰기 -P 16때문에 속도 향상은 선형적이지 않을 수 있습니다 .grep

-P이 답변의 모든 내용은 태그를 제외한 xargs모든 POSIX 시스템에서 실행될 수 있어야 합니다 . -P플래그는 xargs비표준이지만 xargsGNU 및 BSD 시스템에서 구현됩니다.

답변2

다양한 방법의 경우: https://stackoverflow.com/questions/9066609/fastest-possible-grep

그 외에도 이 작업을 자주 수행한다면 SSD가 최선의 선택일 것입니다. HD에 대한 액세스는 이런 종류의 일에 대한 킬러입니다.

실행할 다양한 grep이 있습니다. 스크립트 명령(예: 코어당 하나)을 백그라운드로 실행하는 스크립트를 만든 다음 완료되면 더 많은 명령 실행이 완료되는 시점을 추적합니다.

이렇게 하면 12개 코어를 모두 100% CPU 사용량으로 실행할 수 있지만 리소스 제한은 다른 문제일 수 있습니다. 모든 작업에 동일한 파일이 필요하다는 점을 고려하면 SSD를 사용하지 않는 경우 파일이 공유되지 않도록 복사하는 것이 좋습니다.

답변3

RAM보다 커서 캐시할 수 없는 경우 더 많은 프로세스를 병렬로 실행하는 것이 여러 읽기에 대한 좋은 대안이 /var/log/http/access.log될 수 있습니다. 특히 코어가 여러 개인 경우 더욱 그렇습니다. access.log이렇게 하면 grepIP당 하나(+ 여러 도우미 래퍼 프로세스)가 병렬로 실행됩니다.

pargrep() {
    # Send standard input to grep with different match strings in parallel
    # This command would be enough if you only have 250 match strings
    parallel --pipe --tee grep ^{} '>' /tmp/access-{}.log ::: "$@"
}
export -f pargrep
# Standard input is tee'ed to several pargreps.
# Each pargrep gets 250 match strings and thus starts 250 processes.
# For 1200 ips this starts 3600 processes taking around 1 GB RAM,
# but it reads access.log only once
cat /var/log/http/access.log |
  parallel --pipe --tee -N250 pargrep {} :::: ips

관련 정보