awk를 사용하여 더 적은 메모리를 사용하여 파일을 분할하는 방법

awk를 사용하여 더 적은 메모리를 사용하여 파일을 분할하는 방법

gzip을 사용하여 압축된 nginx 로그인 36GB 파일이 있습니다. 이 작은 파일을 메모리 부족 없이 내가 작성하는 다른 스크립트의 입력으로 사용할 수 있도록 이 파일을 더 작은 파일로 분할하고 싶습니다. 파일의 첫 번째 열에는 IP 주소가 포함되어 있으며 특정 IP 주소의 모든 요청은 분할 후 동일한 파일에 있어야 합니다.

현재 달성해야 할 것은 다음과 같습니다.

#!/bin/bash

FILENAME=$1
NUM_FILES=$(($2))
PREFIX=$3

unpigz -c  $FILENAME | awk -v NUM_FILES=$NUM_FILES -v PREFIX=$PREFIX \
'! ($1 in out) {out[$1] = (idx++ %NUM_FILES) } '\
'{ outfile= PREFIX"." out[$1] ".txt"; print | ("pigz >" outfile) ; next} '

모든 것이 예상대로 작동하지만 메모리를 너무 많이 사용하고 컴퓨터에 여유 공간이 16GB밖에 없어서 xGB보다 큰 파일로 분할할 수 없습니다.

이 경우 어떻게든 메모리를 덜 낭비할 수 있는지 궁금합니다.

답변1

이번 주에는 유니콘과 무지개를 쫓을 것 같습니다. 나는 세 가지 전략에 대한 일정을 개발했습니다. 모든 타이밍에 대해 내 2천만 행을 사용자의 (추정) 80억 행으로 추정하고 있지만 타이밍은 내 노트북과 하드 드라이브에 따라 다릅니다. 타이밍을 맞추기 위해 테스트 파일을 압축했지만 압축 해제에는 gunzip만 사용할 수 있습니다.

(A) 행으로 가득 찬 읽기 메모리(약 10GB)를 클릭한 다음 파일로 스트리밍합니다(한 번에 한 파일씩). 각 IP를 다른 파일에 넣어야 한다는 요구 사항을 오해했습니다. 여기에는 10GB의 데이터마다 약 400,000개의 파일을 추가하고 닫는 작업이 포함됩니다. 내 런닝타임은120시간. 또한 관리할 파일이 400,000개 남게 됩니다. 이 중 어느 것도 좋지 않습니다.

(B) 36GB 파일 다중 처리(압축 해제 시 360GB). 이때 저는 분리하면 500GB의 데이터 전체를 저장할 공간이 부족하다고 오해했습니다.

사실 다중 패스 비용은 그리 비싸지 않습니다. 아이디어는 IP를 인덱스로 그룹화하는 것입니다. 따라서 8번의 패스를 수행하여 각각 100개의 파일, 각 파일에 500개의 IP, 파일당 약 천만 줄, 800개의 파일을 작성할 수 있습니다. 실행 시간은 다음과 같아야합니다.33시간, 다중 패스는 다중 파일 추가보다 훨씬 빠릅니다. 다른 장점도 있습니다:

.. 다음 그룹을 시작하기 전에 그룹의 모든 파일을 압축할 수 있으므로 디스크 공간을 덜 사용할 수 있습니다.

.. 예를 들어 시스템 수요가 낮을 때 야간에 실행되도록 각 그룹을 예약할 수 있습니다.

.. 각 그룹에는 장애 발생 시 재시작 지점이 있습니다.

.. 그룹 파일은 특정 IP를 포함하는 파일을 찾는 데 사용되는 색인입니다.

(C) 당신이 말했듯이, 모든 것의 압축을 푼 복사본을 저장할 만큼 충분한 디스크 공간이 있고 원본 스크립트도 완벽하게 괜찮습니다., 세 가지 사소한 수정이 이루어졌습니다.

.. 프로세스의 모든 출력 파일을 압축할 수는 없지만 GNU 병렬 처리를 사용하여 주의해서 모든 파일에 기록하고 나중에 압축할 수 있습니다.

.. awk 출력 파일 수에서 적절한 타이밍을 찾을 수 있습니다. 경험상 100개의 출력 파일(동일한 전체 데이터 볼륨에 대해)은 80개 또는 120개의 출력 파일보다 빠릅니다. 100개 이상의 방법을 분할하려고 시도하는 것은 현명하지 못할 수 있으므로 각 파일에는 약 8천만 개의 파일 라인과 4000개의 IP가 포함된 100개의 파일이 생길 수 있습니다.

.. 각 IP에 대해 파일 이름을 한 번씩 쓰는 것이 매번 쓰는 것보다 깔끔합니다.

이 접근 방식에 대한 내 추정치는 다음과 같습니다.24 시간.

이제 원본은 다음과 같습니다.

time gunzip -c ../trial.data.gz | awk '
! ($1 in X) { X[$1] = sprintf ("Prefix_%.4d.dat", q++ % 100); }
{ print > X[$1]; }'

답변2

이러한 작업을 수행하는 일반적인 접근 방식은 작업 부하를 분할하고 전체 작업을 두 단계로 수행하는 것입니다(하드 드라이브가 이 방식을 선호할 것입니다).

각 IP 주소(및 메모리에 압축 프로세스가 있는 파이프)에 대한 파일을 생성하는 대신 마지막 IP 옥텟의 각 값에 대한 파일을 생성합니다.

unpigz -c  $FILENAME | awk -v NUM_FILES=$NUM_FILES -v PREFIX=$PREFIX \
'{ last_octet=$0; sub("^[0-9]+\\.[0-9]+\\.[0-9]+\\.","",last_octet); }; '\
! (last_octet in out) {out[last_octet] = 1 } '\
'{ outfile= PREFIX"." last_octet ".txt"; print | ("pigz >" outfile) ; next} '

그런 다음 각 파일에서 코드를 실행합니다. 생성된 파일이 특정 크기에 도달할 때까지(SSD에 저장되지 않은 경우) 메모리에 보관하는 방법을 고려하는 것이 합리적일 수 있습니다.

답변3

이 작업을 수행하기 위한 GNU/awk 스크립트가 있습니다. 그러나 현 시점에서 고려해야 할 성능, 디스크 공간 및 파일 관리와 관련된 여러 가지 문제가 있습니다.

디자인 노트 – 기원.

내 코드를 찾았고 다시 테스트하는 것이 도움이 될 것이라고 생각했습니다. 이것은 간단한 포럼 게시물로 시작되었습니다. 열에 미국 주 코드가 포함된 것으로 알려진 파일이 있는 경우 어떤 주가 존재하는지 알지 못한 채 이를 다른 파일로 분할하는 방법입니다. 간단합니다. 데이터가 언제 도착하는지 알려줍니다. 방금 새로운 주마다 새 파일을 열었습니다. 따라서 60개 이상의 파일(미국 해외 영토 포함)에는 문제가 없습니다.

나는 GNU/awk가 어디까지 갈 수 있는지 알고 싶습니다. 출력 파일 수를 무제한으로 지원하지만 ulimit(1024)에 도달하면 닫았다가 다시 열어 가짜로 만듭니다. ulimit를 늘릴 수 있지만 아마도 400,000까지는 늘릴 수 없습니다. 파일 수가 많고 임의 입력 시퀀스의 경우 이는 한 줄에 하나의 파일을 열고 닫는 것과 비슷합니다(병리학적 상황). 더 빠르고 간단한 솔루션을 찾기 전에 다양한 키 필드(25K의 다양한 값 포함)를 실행하고 다중 패스, 계단식 트리 및 프로세스 트리를 사용하여 다양한 디자인을 시도했습니다.

기본적인 해결책은 가능한 한 많은 행을 가져와서 2D 배열로 채우는 것입니다. 그런 다음 파일 및 원래 순서별로 정렬하여 새로 고칩니다. 이렇게 하면 디스크 조각화가 방지되며 한 번에 하나의 출력 파일만 열면 됩니다.

테스트 데이터.

nginx 웹사이트의 로그 형식을 살펴본 결과 (모든 옵션 필드를 구성하지 않는 한) IP 주소 뒤에 60자의 텍스트가 있다고 가정합니다. 그래서 2천만 행의 데이터(1.5GB)를 연결했습니다.

.. 400,000개의 IP 주소가 확장되고 무작위로 지정됨: {10..29}.{30..39}.{80..129}.{110..149}

.. Harvard CS50 테스트 패키지에서 길이가 40~90자가 아닌 줄을 제외하고 490,306줄의 텍스트를 추출합니다.

두 데이터 모두 2천만 행에 복사된 후 연결됩니다.

평가하다.

성능 - 노트북, 4GB, HDD에서 측정되었습니다.

2천만 행(1.5GB)이 15분 걸렸습니다(압축되지 않은 일반 텍스트 기준). 성능이 O(n)이 아니어야 할 이유가 없으므로 480GB의 원시 데이터는 약 80시간이 걸립니다. 귀하의 서버는 아마도 내 Samsung RV515보다 훨씬 빠를 것입니다.

메모리

내 메모리 배열은 600만 개의 행과 알려진 IP 및 파일 이름의 증분 배열을 사용합니다. 이는 내 4MB 중 약 2.7GB를 사용합니다. 그보다 더 많으면 과도한 스왑 및 REISUB가 발생합니다.

결과물 파일

더 많은 공간을 사용하는 것이 도움이 될 것입니다. 새로 고칠 때마다 출력 파일당 평균 15줄(6M 줄/400K 키)이 추가됩니다. 10배의 메모리를 사용하면 파일 관리의 90%를 피할 수 있습니다.

IP 주소만큼 많은 파일이 있습니다. 한 디렉터리에 40만 개의 파일이 있는데, 처리하기가 매우 까다롭습니다.

입력에서 특정 IP의 분포가 중요할 수 있습니다. 내 데이터는 무작위로 분산되어 있으며 이는 아마도 최악의 시나리오일 것입니다. 사용 패턴이 IP에 로컬로 버스트적으로 액세스한 다음 다시는 액세스하지 않는 것이라면 매번 플러시되는 파일은 더 적고 크기가 커지므로 더 효율적입니다.

400,000개의 파일(한 번에 하나씩)을 추가할 수 있지만 대부분의 컨텍스트가 프로세스에 저장되어 있고 그렇게 많은 프로세스를 실행할 수 없기 때문에 압축된 파일에는 추가할 수 없습니다. 따라서 모든 데이터는 일시적으로 일반 텍스트여야 하며 추출 후 개별적으로 압축해야 합니다. 따라서 일시적으로 500GB의 임시 디스크 공간이 필요합니다.

이 코드를 사용하실 수 있으며, 코드를 맞춤설정하는 데 필요한 도움도 받으실 수 있습니다. 그러나 데이터베이스와 적절한 도구를 사용하기 전에 이러한 유형의 데이터를 분석해야 하며 특히 이것이 일회성 연습 이상인 경우 이를 고려해야 한다고 생각하지 않을 수 없습니다.

관련 정보