2009-12-30 2 views
2

메시지 유형별로 약 6,000 개의 파일로 구문 분석하려는 작은 메시지의 큰 파일 (압축 된 4-5GB)이 있습니다. 메시지는 작습니다. 유형에 따라 5 ~ 50 바이트가 있습니다.하나의 큰 파일에서 읽고 많은 (수십, 수백 또는 수천) 파일을 Java로 작성 하시겠습니까?

각 메시지는 고정 크기 유형 필드 (6 바이트 키)로 시작합니다. '000001'유형의 메시지를 읽으면 을 쓰면 페이로드가 000001.dat에 추가됩니다. 입력 파일에는 메시지가 혼합되어 있습니다. N 개의 동종 출력 파일을 원합니다. 각 출력 파일에는 지정된 유형의 메시지 만 들어 있습니다.

은 무엇인가요? 이 메시지를 매우 많은 개별 파일에 빠르게 기록하는 방법은 무엇입니까? 가능한 한 빨리 처리하기 위해 많은 메모리와 처리 능력을 사용하고 싶습니다. 디스크에 압축 파일이나 압축되지 않은 파일을 쓸 수 있습니다.

메시지 형식 키와 출력 스트림 값이있는 해시 맵을 사용하려고 생각하고 있지만 더 좋은 방법이있을 것입니다.

감사합니다.

+0

우려되는 점은 무엇입니까? – OscarRyz

+0

"효율적인"이란 무엇을 의미합니까? 프로세스에 소요되는 시간을 최적화하려고하십니까? 기억의 양? 파일 시스템에서 개별 읽기 수? 각 출력 파일이 디스크에 기록되는 횟수? 이것을 알면 답을 유도하는 데 도움이됩니다. – delfuego

+1

입력 파일에 '000001'이 여러 번 나타날지 여부를 지정하십시오. 그리고 두 번째 및 다음 번 발생을 의미하는지 여부는 데이터를 '000001.dat' 파일에 연결해야합니까? 이는 가장 효율적인 구현이 무엇이 다른지를 결정합니다. –

답변

4

유닉스 계열 시스템은 일반적으로 주어진 시간에 열려있는 파일 핸들 수에 제한이 있습니다. 내 리눅스에서, 예를 들어, 그것은 현재 1024에 있지만 이유 중에 그것을 바꿀 수는 있습니다. 그러나 열린 파일이 시스템에 부담이되기 때문에 이러한 제한에 대한 충분한 이유가 있습니다.

입력에 동일한 키가 여러 번 발생했는지 여부에 대한 내 질문에 아직 답하지 않았습니다. 즉, 데이터의 여러 개별 일괄 처리를 각 파일에 연결해야 할 수 있습니다. 이것이 사실이 아니라면, Pace의 대답은 당신이 할 수있는 최선의 일일 것입니다. 모든 일이 끝나야하며 그러한 단순한 일련의 사건들에 대해 거대한 행정을 세우는 데는 아무런 의미가 없습니다.

동일한 키에 대한 입력에 여러 메시지가있는 경우 많은 수의 파일을 열어 두는 것이 효율적입니다. 그래도 모든 6000을 한 번에 열어 놓으려고하지 말라고 조언합니다. 대신, 나는 선착순으로 열리는 500과 같은 것을 갈 것입니다. 즉, 처음 500 개의 메시지 키를 열어 파일을 열어 전체 입력 파일을 씹어 500 개에 추가 할 항목을 찾은 다음 입력시 EOF를 치면 모두 닫습니다. 키의 HashSet을 계속 처리해야합니다. 입력 파일을 다시 읽으므로 첫 번째 라운드에서 포착하지 못한 다음 키 500 개를 처리해야하기 때문입니다.

이론적 설명 : 파일 열기 및 닫기는 비용이 많이 드는 작업입니다. 도움이 될만한 파일을 여러 번 열거 나 닫고 싶지는 않습니다. 따라서 가능한 한 많은 핸들을 열어두면 모두 입력을 통해 단일 패스로 채워집니다. 반면에 하나의 입력 파일을 순차적으로 스트리밍하는 것은 매우 효율적입니다. 입력 파일을 12 번 통과해야하더라도 시간은 여섯 번 열고 닫는 데 필요한 시간에 비해 거의 무시할 수 있습니다 파일.

의사 코드 :

processedSet = [ ] 
keysWaiting = true 
MAXFILE = 500 
handlesMap = [ ] 
while (keysWaiting) { 
    keysWaiting = false 
    open/rewind input file 
    while (not EOF(input file)) { 
    read message 
    if (handlesMap.containsKey(messageKey)) { 
     write data to handlesMap.get(messageKey) 
    } else if (processedSet.contains(messageKey) { 
     continue // already processed 
    } else if (handlesMap.size < MAXFILE) { 
     handlesMap.put(messageKey, new FileOutputStream(messageKey + ".dat") 
     processedSet.add(messageKey) 
     write data to handlesMap.get(messageKey) 
    else 
     keysWaiting = true 
    endif 
    } 
    for all handlesMap.values() { 
    close file handle 
    } 
    handlesMap.clear 
} 
+1

나는 이것을 정확하게 쓰고 있었다. 나는 이것이 최선의 선택이라고 생각합니다 - 분열하고 정복하십시오. –

+0

지원해 주셔서 감사합니다. 내 솔루션에 업보가 보이지 않아서 나는 미쳤다고 생각하기 시작했다. +1 문제가 생길 때까지 나는 (거의!) 미안해.) –

+0

처음 500 개의 별개의 레코드 유형이 서로 다른 경우에는 패스 (또는 그 이상)를 낭비 할 수 있음을 명심하십시오. 전체 파일. 이것이 사실 일지는 모르겠지만, 데이터를 보지 않고 말할 수는 없습니다. 당신의 대답은 장점이 있지만, 우리 모두는 프로세스가 실행될 데이터 및/또는 시스템을 보지 않고도 최상의 해결책을 가지고 있다고 말할 수 있습니다. – Rory

4

해시 맵이 필요하지 않을 수 있습니다. 당신은 ...

확실하지 않음 새 파일을

  • 새 파일
  • 에 메시지를 작성하는 메시지
  • 열기 추가 모드에서 새 파일을 읽기 닫기 수 만약 당신이 많은 열리고 닫히기 때문에 이것이 더 빠르다면.

  • +1

    +1, 이것은 간단하고 간단하기 때문에 시도하는 첫 번째 일입니다. 성능이 용납 될 수없는 경우에만 열려있는 출력 스트림을 캐싱하는 것과 같은 좀 더 복잡한 작업을 진행할 것입니다. –

    +0

    좋은 간단한 해결책이지만 일부 출력 파일이 1000s 열리는 경우 이것이 확실하게 빠르지 않습니다! –

    +0

    @Carl : 어떻게 아십니까? 현대 OS (또는 더 정확하게는 OS/파일 시스템 조합)는 몇 초 전에 열린 파일을 다시 열기 위해 물리적 I/O가 필요하지 않습니다. –

    2

    지능형 풀링을 권장합니다. 성능을 향상시키고 리소스를 절약하기 위해 가장 자주 사용하는 파일을 열어 두십시오.

    주 파일이 주로 레코드 유형 1-5로 구성되는 경우 필요에 따라 파일을 열어 두십시오. 필요에 따라 다른 시스템을 열거 나 닫아 자원 시스템을 굶기 지 않게 할 수 있습니다.

    +0

    일부 유형은 대부분의 메시지 (99.9 %)를 차지하므로 좋은 제안입니다. – Rudiger

    +0

    비율을 변경하면 수정할 필요가 없도록 적응성이 높아야합니다. 작성한 각 레코드의 수를 추적하고, 주기적으로 해당 값으로 정렬하고, 더 이상 필요하지 않은 레코드를 닫은 다음 목록 상단에있는 레코드를여십시오. – Rory

    +0

    방금이 댓글을 보았습니다. 나는 내 접근법이이 상황을 "자동적으로"매우 작은 "지능"으로 처리한다고 생각한다. –

    0

    많은 파일에 많은 작은 쓰기를하고 있기 때문에 쓰기의 횟수를 최소화하고 싶습니다. 특히 가장 단순한 디자인은 각각의 새로운 쓰기가 새로운 파일 열기/닫기를 보장한다는 점에서 그렇습니다.

    대신 각 키를 버퍼에 매핑하지 않으시겠습니까? 마지막에는 각 버퍼를 디스크에 씁니다. 또는 너무 많은 메모리를 차지할 염려가 있다면 버퍼가 1K 또는 5K 또는 모든 행을 기록하도록 구조 할 수 있습니다. 예 : SLF4J 같은 더 현대적인 로깅 프레임 워크뿐만 아니라이 기능을 지원하지 않은 경우에도 버퍼 로깅을 사용하도록 구성 할 수 있습니다 별도의 Log4j 로거를 만들 수

    public class HashLogger { 
    
          private HashMap<String,MessageBuffer> logs; 
    
          public void write(String messageKey, String message) 
          { 
           if (!logs.contains(messageKey)) { logs.put(messageKey, new MessageBuffer(messageKey); } 
           logs.get(messageKey).write(message); 
          } 
    
         public void flush() 
         { 
          for (MessageBuffer buffer: logs.values()) 
          { 
           buffer.flush(); 
          } 
          // ...flush all the buffers when you're done... 
         } 
    
        private class MessageBuffer { 
          private MessageBuffer(String name){ ... } 
          void flush(){ .. something here to write to a file specified by name ... } 
          void write(String message){ 
          //... something here to add to internal buffer, or StringBuilder, or whatever... 
          //... you could also have something here that flushes if the internal builder gets larger than N lines ... 
        } 
    } 
    

    , 나는 놀랄 것입니다.

    +0

    심각하게,이 문제에 대한 로깅 프레임 워크 ??? – erikkallen

    +0

    게시글을 사용하면 포스터가 문제의 가장 단순한 정의를 제공한다고 생각하는 것이 좋습니다. 나는 사람들에게 세부 사항을 지루하게하고 싶지 않기 때문에 내가하는 것을 안다. 어쩌면 포스터의 경우 기본 제공 메시지 서식 때문에 더 나은 옵션 일 수 있습니다. 도대체 무엇이. –

    1

    나는 당신의 질문에 대한 몇 가지 가정을 만들려고 해요 :

    • 각 메시지는 고정 된 크기의 필드로, 메시지 유형으로 시작
    • 메시지가 혼합되어있는 이질적인 입력 파일이 있습니다. N 개의 동질적인 출력 파일을 원한다. 각 출력 파일에는 주어진 유형의 메시지 만 들어있다.

    마음에 점프하는 접근 방법은 functor 기반입니다. 특정 메시지를 처리하는 객체에 메시지 유형 매핑을 만듭니다. main()은 고정 메시지 헤더를 읽고 맵에서 적절한 펑터를 찾은 다음 디스패치 루프를 호출하는 디스패치 루프입니다.

    한 번에 6,000 개의 파일 (메시지 유형마다 하나씩)을 열 수는 없습니다. 대부분의 운영 체제에는 약 1,024 개의 동시 열린 파일의 제한이 있습니다 (Linux에서는이를 제어하는 ​​커널 매개 변수를 변경할 수 있음). 따라서 이것은 파일을 반복해서 열고 닫을 것임을 의미합니다.

    아마도 가장 좋은 방법은 모든 functor에 고정 계수 버퍼를 설정하여 10 개의 메시지를 열고, 쓴 후 닫는 것입니다. 메시지가 최대 50 바이트이면 500 바이트 (10 x 50) x 6,000이며 지정된 시간에 메모리에 남아 있습니다.

    아마 고정 된 크기의 바이트 배열을 유지하고, 그 배열로 한 번에 N 바이트를 읽는 일반적인 펑 클래스를 만들 내 펑터를 써서 :에 열려있는 파일에 일반적으로 한계를있다

    public class MessageProcessor 
    { 
        int _msgSize;     // the number of bytes to read per message 
        byte[] _buf = new byte[1024]; // bigger than I said, but it's only 6 Mb total 
        int _curSize;     // when this approaches _buf.length, write 
    
    0

    시스템 및 임의의 경우에 임의의 순서로 수천 개의 작은 파일에 액세스하면 시스템이 매우 나 빠지게됩니다.

    대용량 파일을 개별 메시지의 파일 (또는 메모리가있는 경우 일종의 메모리 내 테이블)로 분해하고 메시지 유형별로 정렬하는 것을 고려하십시오. 완료되면 해당 파일에 메시지를 작성하십시오.

    관련 문제