하둡에서 InputSplit과 HDFS 블록 사이의 관계

예를 들어 hadoop의 TextInputFormat 을 보자. 이 포맷은 하둡의 default input format인데, 파일에서 text들을 라인 단위로 읽어서 map task에게 제공해주는 역할을 한다. (참고로 key는 파일 내에서 각 라인의 시작 지점까지의 바이트 오프셋 값이다.)

여기서 당연히 한 가지 의문이 생기는데, 기본적으로 InputSplit은 HDFS의 블록으로 쪼개진다. (특별히 따로 InputSplit을 정의하지 않았다면) 그럼 HDFS block boundary가 TextInputFormat의 line boundary와 정확히 일치하지 않을 텐데, hadoop에서는 이를 어떻게 처리하고 있을까?

Hadoop: The Definite Guide 책을 보면, 아래와 같은 그림이 나온다.

그리고 설명하기를, block boundary와 input split의 논리적인 단위 (여기서는 line 단위)가 일치하지 않더라도, input split을 잘 계산하여 처리한다고 나와 있다. 이 설명만 놓고 보면, 마치 InputFormat에서 InputSplit을 계산할 때, 이런 논리적인 단위가 나눠지는 것처럼 보인다.

하지만 실제로 InputSplit을 구현한 FileSplit을 코드를 보면, 아래와 같은 정보만을 가지고 있다.

  • private Path file;   // 어떤 file에서 나온 split인가
  • private long start; // file 내에서 해당 input split의 시작 위치
  • private long length;  // input split의 크기
  • private String[] hosts; //  해당 input split을 가지고 있는 data node들의 host name

그리고 이런 FileSplit을 계산해 내는 FileInputFormat 클래스 소스를 살펴보면, 논리적인 단위를 계산하는 부분은 없고, 단지 file을 block boundary 단위로 나누어, FileSplit을 계산한다. 즉, FileSplit이 가지고 있는 정보는, 논리적인 단위가 아닌, 단순히 물리적인 block의 경계에 대한 정보만 가지고 있다. 그렇다면 실제로 block 단위가 아닌, line 단위로 input split을 재계산 해 주는 녀석은 누구일까?

비밀은 바로 RecordReader에 숨어 있다. InputFormat interface는 getSplits() 메소드를 통해 split을 계산해내고, getRecordReader() 메소드를 통해 해당 split을 읽을 수 있는 RecordReader를 반환한다.

그리고 각 MapTask가 실행될 때, map() 메소드를 호출하기 전에 RecordReader의 initialize() 메소드를 한 번 호출한다. 그리고 TextInputFormat이 사용하는 LineRecordReader의 initilize() 메소드를 살펴보면, map task로 전달된 input split이 파일의 첫 번째 block이 아닐 경우, 무조건 첫 번째 line은 skip하도록 구현되어 있다. (start position에 -1을 한 다음, 한 줄을 건너뜀) 이제 저 위 그림이 이해가 될 것이다.

정리를 하면, file을 처리하는 InputSplit은 단순히 물리적인 block 단위 (기본적으로는 hdfs block 단위)로 쪼개져서 map task에게 전달된다. 그리고 각 map task는 이렇게 쪼개진 input split으로부터, 실제로 map() 에서 처리해야 하는 논리적인 record 단위를 계산하고, 이를 바탕으로 InputSplit의 boundary를 다시 계산하여 처리를 하게 되는 것이다.

댓글

Designed by JB FACTORY