데이터 엔지니어( 실습 정리 )/hadoop

3. 하둡 분산 파일 시스템 ( 하둡 기초 )

세용용용용 2025. 9. 15. 21:44

[ "하둡 완벽 가이드 4판" ] 공부한 후 정리한 내용 입니다

 

 

3. 하둡 분산 파일 시스템

분산 파일 시스템 : 네트워크로 연결된 여러 머신의 스토리지를 관리하는 파일 시스템


1) HDFS 설계

하둡은 HDFS 라는 분산 파일 시스템을 사용
→ 대용량 파일을 여러 서버에 나눠 저장하고, 빠르게 순차 읽기가 가능하도록 설계됨.

 

- HDFS가 잘 맞지 않는 응용 분야

  • 빠른 응답 시간 요구
    → HDFS는 처리량(Throughput)에 최적화. (실시간 응답은 HBase가 대안)
  • 수많은 작은 파일
    → 네임노드 메모리에 파일 메타데이터를 저장해야 하므로 파일이 많아지면 메모리 부담이 큼.
  • 다중 라이터, 임의 수정 불가
    → 기본적으로 한 번 쓰거나, 파일 끝에 Append만 가능.
    → Hadoop 3.0부터 다중 라이터 일부 지원.

단일 라이터 : 한 파일에 한 번에 한 사람만 쓰기 가능

다중 라이터 : 한 파일에 여러 사람이 동시에 쓰기 가능


 

 

 

2) HDFS 개념


2.1 블록

  • HDFS에서 데이터를 저장하는 최소 단위 (기본 128MB).
  • 파일은 여러 블록으로 쪼개져 분산 저장됨.

 

특징

  • 작은 파일도 크기만큼만 저장 → 공간 낭비 없음.
  • 블록 단위 저장 덕분에 → 내고장성, 가용성, 확장성 제공.
  • HDFS 블록이 큰 이유 : 탐색 비용 최소화 ( 탐색을 줄이고 데이터 전송에 더 많은 시간 할애 가능 )

 

이점

  • 단일 디스크 크기보다 큰 파일 저장 가능.
  • 블록 단위 추상화로 스토리지 구조 단순화.
  • 블록 복제(Replication)로 장애 대응.

 

 

블록 상태 확인 명령

hdfs fsck / -files -blocks

 

내고장성 : 일부 디스크 고장시 데이터 손실 없이 계속 사용

가용성 : 데이터가 항상 접근 가능하도록 복제/분산되 있어 읽기, 쓰기 서비스가 중단되지 않음


2.2 네임노드 & 데이터 노드

  • hdfs 기본 구성 : 네임노드 2, 데이터 노드 n

 

네임노드 (관리자)

  • 파일 시스템 네임스페이스 관리 (메타데이터 저장), 네임스페이스 이미지, edit 로그 두 종류 파일 네임노드 로컬 디스크 영속 저장
  • 네임스페이스 이미지 ( HDFS 전체 스냅샷 ), edit 로그 ( fsimage 이후 발생한 모든 변경 기록 ) >> 주기적 병합
  • "어떤 파일이 어떤 블록으로 나뉘어, 어느 데이터노드에 있는지" 추적.
  • 장애 시 HDFS 전체가 멈춤 ( 데이터 노드에 실질적 블록이 있지만 블록 정보만으로 파일 재구성을 할수 없음.. ) → 장애 복구 필수

 

데이터노드 (일꾼)

  • 실제 블록을 저장하고 요청 시 제공.
  • 블록 목록을 네임노드에 주기적으로 보고.

 

hdfs 클라이언트

  • 사용자는 open(), read(), write()만 호출하면 됨.
  • 블록 위치, 복제, 네트워크 전송은 클라이언트가 자동 처리.
  • 즉, 사용자는 복잡한 HDFS 내부 구조 몰라도 코드 작성 가능

2.3 블록 캐싱

  • HDFS에서 자주 읽는 블록을 데이터노드 메모리에 올려두는 기능입니다.
  • 보통은 블록을 디스크에서 읽는데, 캐싱하면 메모리에서 즉시 읽기 가능 → 훨씬 빠름.

 

특징

  • 단위: 블록 단위로 캐싱되지만, 사용자는 파일 단위로 캐시 설정 가능.
  • 위치: 블록은 한 데이터노드 메모리에만 올라감. (2, 3 복제본 블록 캐싱 안함, 모든 노드에 올려두는 게 아님)
  • 관리: HDFS가 어떤 블록이 캐시되었는지 정보를 유지.

 

동작 방식

  1. 사용자가 특정 파일을 캐시하도록 지시(명령)함.
  2. 데이터노드가 해당 블록을 메모리에 적재.
  3. 클라이언트가 파일 읽기 요청 시:
    • HDFS는 "이 블록이 캐시되었는지" 확인.
    • 캐시된 경우 → 메모리에서 읽음 (빠름).
    • 없는 경우 → 디스크에서 읽음 (느림).
  4. Spark/MapReduce 잡 스케줄러는 캐시된 블록이 있는 데이터노드에서 우선 작업 실행 → 데이터노드 메모리에서 읽어 디스크 I/O 부하를 줄임

 

장점

  • 반복해서 접근하는 데이터 읽기 성능을 크게 향상.
  • 데이터 노드 메모리에서 읽으므로 디스크 I/O 감소 ( 디스크 seek + read 시간 사라 ) 
  • 특히, 반복적 쿼리/잡에 유리.

 

활용 사례 ( 작은 Lookup 테이블 캐싱 )

  • 예: SQL 조인 시, 큰 테이블 A와 작은 테이블 B가 있을 때
  • 작은 테이블 B를 블록 캐시에 올려두면 → 디스크 대신 메모리에서 바로 읽음.
  • → 반복적으로 조인해도 속도 훨씬 빨라짐.

 

캐시 지시자

  • 특정 파일이나 디렉터리를 캐시 대상으로 지정.
  • HDFS는 해당 파일의 블록을 데이터노드 메모리에 유지.

[ 명령어 예시 ]

hdfs cacheadmin -addDirective -path /user/data/small_table -pool pool1
→ /user/data/small_table 파일을 pool1 캐시 풀에 등록.

 

 

캐시 풀

  • 캐시 리소스를 관리하는 그룹 단위 관리 시스템.
  • 여러 사용자/잡이 캐시를 공유하거나 접근 권한 제어 가능.
  • 관리자 입장에서, "누가 어떤 파일을 캐시할 수 있는지" 통제할 수 있음.

2.4 HDFS 페더레이션

  • 하나의 네임노드가 모든 메타데이터를 관리하면 병목이 생기므로, 여러 네임노드가 서로 다른 네임스페이스(파일 시스템 영역) 를 나눠서 관리하는 구조. → 확장성과 성능 향상.
  • 예시
    • NameNode A → /user 디렉터리 관리
    • NameNode B → /share 디렉터리 관리

 

구성 요소

  • 네임스페이스 볼륨: 각 네임노드가 관리하는 메타데이터 공간. 서로 독립적.
  • 블록 풀: 실제 파일 데이터를 담는 블록 집합. 데이터노드가 여러 블록 풀을 동시에 관리. → 네임스페이스와는 독립적이지 않음

 

데이터 노드 등록

  • 모든 데이터노드는 클러스터 내 모든 네임노드에 등록.
  • 따라서 하나의 데이터노드가 여러 네임스페이스의 블록을 저장할 수 있어, 데이터 분산 + 확장성이 좋아짐.

정리 : 여러 네임노드가 영역별로 관리 / 데이터노드는 모든 블록 저장 → 확장성과 성능 최적화.


2.5 HDFS 고가용성 ( HA )

  • Hadoop 2.x부터 지원.
  • 기존 단일 네임노드 구조의 단점을 해결 → 장애 시 자동 복구 가능.

 

HA를 위한 구조 변경

  1. 네임노드 공유 스토리지
    • EditLog를 공유 저장소(저널노드)에 기록.
    • Active와 Standby가 동일한 상태 유지.
  2. 데이터노드 블록 리포트
    • 두 네임노드 모두에게 블록 정보를 보고.
  3. 클라이언트 로직 개선
    • Active/Standby 전환 시 자동으로 올바른 네임노드로 요청.
  4. Standby 작업
    • 스탠바이 네임노드는 주기적으로 네임스페이스 체크포인트 생성 → 장애 대비.

 

QJM ( Quorum Journal Manager )

  • HA 전용 공유 스토리지 방식.
  • 저널노드(JournalNode) 그룹에 EditLog를 동시에 기록.
  • 과반수 노드에 기록 성공해야 유효.
  • 안정성과 일관성 확보.

 

저널 노드( JournalNode )

  • 공유 저널 스토리지 역할.
  • 최소 3대 이상 구성 권장 (Quorum 구조).
  • Active NN이 기록하면 Standby NN도 같은 로그를 읽어 동기화.

 

장애 복구

  • Failover Controller
    • 각 NN마다 동작하며 하트비트로 상태 감시.
    • Active 장애 시 Standby를 Active로 승격.
  • 펜싱(Fencing)
    • 장애 난 기존 Active가 다시 영향력을 행사하지 못하도록 강제로 종료.
    • 방법 예시: SSH 접속 후 프로세스 kill.

 

정리: Active-Standby 구조 + QJM + Failover Controller + Fencing 으로 네임노드 장애 시 서비스 중단 없이 자동 복구.


 

 

 

3) 명령행 인터페이스


3.1 핵심 설정

1. 기본 파일 시스템 (fs.defaultFS) → 기본 HDFS 주소 설정

  • 기본값: hdfs://localhost/ ( ex.. 예시 hdfs://sycluster )
  • 역할:
    • HDFS 데몬 → 네임노드 호스트와 포트 결정
    • HDFS 클라이언트 → 네임노드 주소 확인

2. 레플리카 설정 (dfs.replication)

  • 기본값: 3
  • 파일 저장 시 복제본(레플리카) 개수를 의미

3.2 자주 쓰는 명령어

1. 로컬 → HDFS 업로드

hadoop fs -copyFromLocal /sy0218/sy.txt /user/sy/sy.txt

 

2. HDFS → 로컬

hadoop fs -copyToLocal /user/sy/sy.txt sy.copy.txt

 

 

 

4) 하둡 파일 시스템

  • 하둡은 여러 종류의 파일 시스템을 통합적으로 다룰 수 있는 "추상화 인터페이스" 제공
  • 즉, hdfs, 로컬, 클라우드 스토리지도 동일한 방식으로 접근 가능!!

4.1 기본 개념

  • org.apache.hadoop.fs.FileSystem: 하둡 파일 시스템 접근을 위한 클라이언트 인터페이스
  • URL 스킴을 통해 어떤 파일시스템 쓸지 선택
# 로컬
hadoop fs -ls files:///

# 하둡 분산 파일 시스템
hadoop fs -ls hdfs:///

# Aws s3
hadoop fs -ls s3a://[ 버킷 명 ]/

4.2 주요 파일 시스템 구현체

[ 스킴 ] [ 클래스 ] [ 설명 ]
file fs.LocalFileSystem 로컬 디스크용, 체크섬 사용
hdfs hdfs.DistributedFileSystem 하둡 분산 파일 시스템
webhdfs hdfs.web.WebHdfsFileSystem HTTP 기반 HDFS 접근
har fs.HarFileSystem 아카이브(HAR) 파일 지원
viewfs viewfs.ViewFileSystem 페더레이션 네임노드 마운트 시 사용
ftp fs.ftp.FTPFileSystem FTP 서버 연결
s3a AWS S3 아마존 S3 지원
wasb Azure Storage 마이크로소프트 Azure 지원
swift OpenStack Swift 오픈스택 Swift 지원

4.3 하둡 파일 시스템 인터페이스 ( Java API vs WebHDFS )

1. java API

  • 프로토콜: 하둡 내부 바이너리 프로토콜(RPC) + 내부 직렬화 사용
  • 연결 방식: 네임노드와 통신 후 데이터노드와 직접 연결
  • 성능: 빠름 → 대용량 파일 처리에 유리

 

2. WebHDFS

  • 프로토콜: HTTP + REST 기반
  • 연결 방식:
    1. 클라이언트 → 네임노드 요청
    2. 네임노드 → 데이터노드 위치 전달
    3. 클라이언트 → 데이터노드 요청 → 파일 전송
  • 성능: 느림 → HTTP 통신 오버헤드와 리디렉션 단계 때문

 

3. WebHDFS 느린 이유!!

  1. HTTP 통신 오버헤드
    • REST API 방식으로, 요청마다 HTTP 헤더와 메타데이터 전송/파싱 필요
    • JSON, XML 응답 처리 때문에 추가 리소스 사용
  2. 2단계 리디렉션 구조
    • 네임노드 → 데이터노드 위치 → 데이터 전송
  3. 데이터 전송 효율
    • Java API: DFSClient 통해 소켓 기반 고속 전송 하둡 내부 바이너리 직렬화 사용 → 패킷 크기 최소화
    • WebHDFS: HTTP 스트리밍 사용 → 낮은 전송 효율, 리소스 추가 사용 텍스트 헤더 + URL + 상태 코드 포함 → 동일 데이터라도 더 많은 바이트 전송

 

정리: 대용량 파일 전송이나 성능이 중요한 작업에는 Java API가 유리

 

  • RPC = 바이너리 + 최소 메시지 + 스트리밍 최적화 → 빠름
  • HTTP = 텍스트 + 헤더/리디렉션 + REST 호출 → 느림

 


 

 

 

5) 자바 인터페이스 ( 하둡 파일 시스템 )

1. FileSystem ( 추상 클래스 )

  • 무엇?
    하둡에서 파일을 읽고 쓰고 삭제하는 등 공통 작업을 정의한 기본 틀(인터페이스)
  • 주요 기능:
    • open() : 파일 읽기
    • create() : 파일 생성
    • delete() : 파일 삭제
    • listFiles() : 파일 목록 조회
  • 특징:
    • HDFS 뿐만 아니라 로컬, S3, Azure 등 다양한 저장소와 호환 가능
    • 코드가 특정 저장소에 종속되지 않도록 도와줌

 

2. DistributedFileSystem ( HDFS 구현체 )

  • 무엇?
    FileSystem을 상속받아 HDFS 전용으로 동작하도록 만든 구현체
  • 동작 방식:
    • 네임노드(NameNode)와 데이터노드(DataNode)와 직접 통신
    • HDFS 파일 읽기/쓰기/삭제 수행

 

3. Why?? FileSystem을 기준으로 코드 작성해야됨??

  • 만약 코드에서 DistributedFileSystem만 사용하면 → 나중에 다른 저장소(S3, 로컬 등)로 바꾸기 어려움
  • FileSystem 기준으로 코드를 작성하면 → 설정만 바꿔도 HDFS ↔ S3 ↔ 로컬 등 호환성 유지 가능

5.1 하둡 URL로 데이터 읽기

import java.io.InputStream;
import java.net.URL;
import org.apache.hadoop.fs.FsUrlStreamHandlerFactory;
import org.apache.hadoop.io.IOUtils;

public class URLCat {

    // 프로그램 시작 시 단 한 번 실행되는 static 블록
    static {
        // 하둡 파일 시스템 URL 스킴 등록
        // "hdfs://", "file://", "s3a://" 등을 URL로 읽을 수 있게 함
        URL.setURLStreamHandlerFactory(new FsUrlStreamHandlerFactory());
    }

    public static void main(String[] args) throws Exception {
        InputStream in = null; // 파일 입력 스트림 변수
        try {
            // args[0] : 실행 시 전달된 URL (예: hdfs://localhost:8020/user/file.txt)
            // URL 객체 생성 → 지정한 파일 위치를 가리킴
            in = new URL(args[0]).openStream(); // 파일 읽기 스트림 열기

            // 파일 내용을 콘솔(System.out)으로 출력
            // 4096 바이트 버퍼 사용, false → 스트림 닫지 않음
            IOUtils.copyBytes(in, System.out, 4096, false);

        } finally {
            // 입력 스트림을 안전하게 닫음 → 메모리 누수 방지
            IOUtils.closeStream(in);
        }
    }
}


--- 실행 예시 ---
export HADOOP_CLASSPATH=hadoop-examples.jar
hadoop URLCat hdfs://localhost/user/sy/sy.txt

--- 출력 결과 ---
세용용세용
바보보바보

5.2 파일 시스템 API로 데이터 읽기

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IOUtils;

import java.io.InputStream;
import java.net.URI;

public class FileSystemCat {
    public static void main(String[] args) throws Exception {

        // 실행 시 첫 번째 인자로 파일 경로를 받음
        // 예: hdfs://localhost:8020/user/sy/sy.txt
        String uri = args[0];

        // Hadoop 설정 객체 생성
        Configuration conf = new Configuration();

        // FileSystem 객체 생성
        // URI와 conf를 기반으로 HDFS, 로컬, S3 등 알맞은 구현체 반환
        FileSystem fs = FileSystem.get(URI.create(uri), conf);

        InputStream in = null;
        try {
            // Path 객체로 파일 열기
            // HDFS, 로컬 등 지정된 경로의 파일 읽기
            in = fs.open(new Path(uri));

            // 파일 내용을 콘솔(System.out)으로 출력
            // 4096 바이트 버퍼 사용
            // false → 스트림 자동 닫지 않음
            IOUtils.copyBytes(in, System.out, 4096, false);

        } finally {
            // 입력 스트림 안전하게 닫기 → 메모리 누수 방지
            IOUtils.closeStream(in);
        }
    }
}


--- 실행 예시 ---
hadoop FileSystemCat hdfs://localhost/user/sy/sy.txt

--- 출력 결과 ---
바보보
세이용

5.3 데이터 쓰기

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IOUtils;
import org.apache.hadoop.util.Progressable;

import java.io.*;

public class FileCopyWithProgress {

    public static void main(String[] args) throws Exception {
        // -------------------------
        // 1. 입력값 처리
        // args[0] : 로컬 파일 경로
        // args[1] : HDFS 목적지 경로
        // -------------------------
        if (args.length != 2) {
            System.out.println("사용법: hadoop FileCopyWithProgress <local_src> <hdfs_dst>");
            return;
        }

        String localSrc = args[0];  // 로컬 파일 경로
        String dst = args[1];       // HDFS 목적지 경로

        // -------------------------
        // 2. 로컬 파일 입력 스트림 생성 (버퍼 적용)
        // -------------------------
        InputStream in = new BufferedInputStream(new FileInputStream(localSrc));

        // -------------------------
        // 3. Hadoop 설정 객체 생성
        // -------------------------
        Configuration conf = new Configuration();

        // -------------------------
        // 4. HDFS 파일시스템 객체 생성
        // -------------------------
        FileSystem fs = FileSystem.get(new java.net.URI(dst), conf);

        // -------------------------
        // 5. 출력 스트림 생성 + Progressable 구현
        //    → 복사 진행 시 "." 출력
        // -------------------------
        OutputStream out = fs.create(new Path(dst), new Progressable() {
            public void progress() {
                System.out.print("."); // 복사 중 진행 표시
            }
        });

        // -------------------------
        // 6. 파일 복사
        //    - 버퍼 크기: 4096 바이트
        //    - 마지막 true: 스트림 자동 닫기
        // -------------------------
        IOUtils.copyBytes(in, out, 4096, true);

        System.out.println("\n파일 복사 완료: " + dst);
    }
}

- HDFS는 대용량 데이터를 안정적으로 쓰기 위한 구조 때문에, 중간 수정과 무작위 쓰기를 지원하지 않고, 순차적 쓰기와 append만 가능!!


5.4 디렉터리

FileSystem fs = FileSystem.get(conf);
fs.mkdirs(new Path("/user/hadoop/mydir"));

 

  • mkdirs(Path f) : 디렉터리를 생성하는 메서드
  • 특징: 부모 디렉터리가 없어도 자동 생성

5.5 파일 시스템 질의

- 파일 메타 데이터 : FileStatus

>> 하둡 파일 시스템 파일 상태를 확인 하고 싶을떄 사용

  • HDFS 파일이나 디렉터리의 메타데이터 조회 가능
  • FileStatus 클래스 주요 정보:
    • 파일 길이 (Size)
    • 블록 크기 (Block size)
    • 복제 수 (Replication)
    • 수정 시간 (Modification time)
    • 소유자/그룹, 권한 정보
FileStatus status = fs.getFileStatus(new Path("/user/hadoop/myfile.txt"));
System.out.println("파일 크기: " + status.getLen());
System.out.println("복제 수: " + status.getReplication());

5.6 데이터 삭제

public boolean delete(Path f, boolean recursive) throws IOException
  • 반환값: boolean → 삭제 성공 시 true, 실패 시 false
  • 파라미터
    • Path f : 삭제할 파일 또는 디렉터리 경로
    • boolean recursive : 디렉터리 안 내용까지 삭제할지 여부
  • 예외 처리: IOException → 파일시스템 접근 실패, 권한 문제 등..

 

 

 

6) 데이터 흐름

- 하둡 파일 시스템에서 파일 읽고 쓰는 과정은 "클라이언트 → 네임노드 → 데이터노드"가 서로 협력해 진행


6.1 파일 읽기 ( FSDataInputStream )

FSDataInputStream 아키텍쳐

1단계: 파일 열기

  • 클라이언트는 DistributedFileSystem.open() 호출
  • 파일 열기 → FSDataInputStream 반환
  • FSDataInputStream: 사용자 입장에서 read()만 호출하면 데이터 읽기 가능
  • 내부적으로 DFSInputStream이 실제 네임노드/데이터노드와 통신

 

2단계: 블록 위치 조회

  • HDFS 파일은 블록(Block) 단위로 나눠 저장됨 (보통 128MB 또는 256MB)
  • 클라이언트 → 네임노드 RPC 호출
  • 네임노드 → 각 블록 위치와 복제본 데이터노드 목록 전달
  • 클라이언트는 가까운 데이터노드부터 순서대로 연결
  • 클라이언트가 데이터노드가 위치한 서버라면 로컬에서 바로 읽음 → 속도 향상

 

3단계: 데이터 읽기

  • FSDataInputStream → DFSInputStream을 사용
  • DFSInputStream → 블록 단위로 데이터노드와 연결, I/O 처리
  • 클라이언트는 read() 반복 호출 → 데이터를 스트림처럼 전송 받음
  • 블록 끝 도달 → DFSInputStream 연결 종료, 다음 블록 데이터노드 조회 → 연결 반복

 

읽는 도중 장애 처리

  • 데이터노드 장애 발생 → 다른 블록 복제본과 연결
  • 전송 중 데이터 체크섬 검증체크섬 손상 시 다른 복제본에서 읽음
  • 손상 블록 정보 → 네임노드에 동기화 → 차후 복제 생성

 

읽기 과정 핵심 포인트

  1. 클라이언트가 직접 데이터노드와 통신 → 트래픽 분산
  2. 네임노드: 메타데이터 관리만 → 데이터 전송 X, 병목 최소화
  3. 장애 대응 → 블록 복제본 활용
  4. 동시에 여러 클라이언트 읽기 가능

6.2 파일 쓰기 ( FSDataOutputStream )

FSDataOutputStream 아키텍쳐

1단계: 파일 생성

  • 클라이언트 → DistributedFileSystem.create() 호출
  • 네임노드 → 동일 파일 존재 여부, 권한 확인
  • 실패 시 → IOException 발생
  • 성공 시 → FSDataOutputStream 반환 (내부적으로 DFSOutputStream 사용)

 

2단계: 데이터 쓰기

  • DFSOutputStream → 데이터를 작은 패킷으로 나눠 Data Queue에 저장
  • DataStreamer → 패킷을 가져와 파이프라인 구성 후 데이터노드로 전송
  • 파이프라인: 첫 번째 → 두 번째 → 세 번째 데이터노드 순서로 전송
  • ACK 큐 관리: 각 패킷이 모든 데이터노드에서 승인 받아야 ACK Queue에서 제거

 

장애 처리

  • 파이프라인 실패 → ACK 큐 패킷을 Data Queue 앞쪽으로 되돌려 재전송
  • 정상 데이터노드는 새로운 블록 ID 받아 전송 재개
  • 장애 데이터노드는 재기동 시 불완전 블록 삭제
  • 네임노드는 불완전 블록 인식 → 차후 복제본 생성
  • 최소 복제 수(dfs.namenode.replication.min) 이상 블록 전송 완료 시 → 쓰기 성공

 

3단계: 파일 닫기

  • 클라이언트 → FSDataOutputStream.close() 호출
  • 파이프라인에 남아 있는 모든 패킷 flush + ACK 대기
  • 최종 전송 완료 후 → 네임노드에 파일 완료 신호 전달
  • 네임노드 → 블록 복제 완료 확인 후 최종 성공 반환

 

쓰기 과정 핵심 포인트

  1. 한 파일 = 단일 writer
  2. DFSOutputStream + DataStreamer → 블록 단위 전송
  3. 장애 복구 → 실패 블록 재전송, 불완전 블록 삭제, 네임노드가 복제 처리
  4. close() → 모든 패킷 flush + 최종 블록 복제 확인

6.3 일관성 모델

  • 파일 시스템 일관성 모델 : 파일에 데이터 쓸 떄, 언제&어떤 순서로 보이는가를 정한 규칙
  • POSIX(리눅스/유닉스 표준)
    • 데이터를 쓰자마자 다른 프로세스에서 읽을 수 있음
    • close() 전에 이미 쓴 부분도 읽기 가능

 

HDFS는 왜 다를까?

  • 목적: 대용량 데이터 분산 저장
  • POSIX 처럼 즉시 읽기 가능하게 하면 성능이 크게 떨어짐
  • 그래서 “쓰기 완료 후 일관성” 모델 채택 → 완전히 쓰여야 다른 클라이언트가 볼 수 있음

 

HDFS의 일관성 동작 예시

1. 파일 생성
- 네임노드에서 파일 존재는 바로 확인 가능
- 하지만 내용은 아직 없음

2. 데이터 쓰기 진행 중
- 파일이 여러 블록으로 나뉘어 기록됨
- 이미 기록이 끝난 블록은 다른 리더가 읽을 수 있음
- 현재 쓰고 있는 블록은 아직 읽을 수 없음
	→ 즉, 블록 단위로 가시성이 생김!!
	예시: 파일이 3블록
	블록1 기록 완료 → 리더 읽기 가능
	블록2 기록 중 → 리더는 블록2 읽을 수 없음
	블록3 기록 전 → 리더는 아직 블록3 없음
    
3. 파일 닫기 (close())
- HDFS 내부적으로 hflush() 자동 실행
- 기록 완료된 블록은 모두 읽기 가능

4. close() 호출 후
- 내부적으로 hflush() + hsync() 보장
- 네임노드가 파일을 COMPLETE 상태로 마킹 ( 모든 블록 읽기 가능 + 정상 파일로 취급 )

 

 

hflush() vs hsync()

기능 설명 특징
hflush() 데이터노드 메모리 버퍼까지 플러시 읽을 수 있음, 전원 다운 시 유실 가능
hsync() 데이터노드 디스크까지 동기화 fsync()와 유사, 강력한 안전성, 성능 저하

 

  • 파일 닫기(close()) = 암묵적 hflush() 실행
  • 성능 vs 안전성의 트레이드오프 존재

 

설계 팁

  • 중간중간 안전하게 저장하려면 적절히 hflush(), hsync() 호출
  • 너무 자주 → 안전하지만 성능 ↓
  • 너무 드물게 → 빠르지만 장애 시 데이터 손실 위험 ↑
  • 최적 균형점 찾아 처리 속도와 안정성 조정

 

포인트

- HDFS는 블록 단위로 기록 완료 시점에 가시성을 제공

  • 완전히 쓰기 완료해야 전체 파일을 읽는 것이 아니라
  • 이미 쓰여진 블록은 읽을 수 있음 → 블록 단위 스트리밍 가능

가시성 : 쓴 데이터가 다른 애 눈에 언제보이지

트레이드오프 : 어떤 걸 얻으면 다른 걸 포기해야하는 관계 ( ex.. 지원이 초밥 사주기 - 사랑을 얻지만 지갑은 포기 ㅋㅋ )


 

 

 

 

7) distcp 를 사용한 HDFS 데이터 복사 & 클러스터 균형 관리


7.1 distcp란?

  • 대량 데이터를 병렬로 HDFS에 복사하기 위한 도구
  • 단순 hadoop fs -cp 대신 사용 → 대규모 파일/디렉터리 복사에 최적화
  • 내부적으로 MapReduce 잡으로 실행 (리듀스 없음)
1. 파일/디렉터리 단순 복사
hadoop distcp file1 file2

2. 변경된 파일만 복사
hadoop distcp -update dir1 dir2

3. 클러스터 간 데이터 이동 + 삭제 옵션
## -delete : 타깃 경로에만 존재하는 파일 지우는 옵션
hadoop distcp -update -delete -p hdfs://namenode1/foo hdfs://namenode2/foo

4. 버전이 다른 클러스터 간 복사
## WebHDFS 사용
hadoop distcp webhdfs://namenode1:50070/foo webhdfs://namenode2:50070/foo
  • 기본적으로 20개 맵이 사용되며 -m 옵션으로 조정 가능

7.2 HDFS 클러스터 균형 유지

  • HDFS는 데이터를 여러 노드에 분산해야 성능과 안정성 유지
  • 그런데 distcp로 맵을 1개만 사용(-m 1)하면:
    • 첫 번째 복사본 블록이 특정 노드에 몰림 → 불균형 발생

해결 방법

  1. 맵(Map) 개수 늘리기
    • 여러 노드에서 동시에 복사 수행 → 블록이 여러 노드에 퍼짐
    • 균형 향상, 처리 속도 증가
    • 단점: 너무 많이 늘리면 다른 잡(Job) 영향 있음
  2. HDFS Balancer 사용
    • 클러스터 전체 스캔 후, 데이터가 몰린 노드에서 여유 노드로 블록 이동
    • 점진적으로 균형 회복
    • 단점: 시간이 걸림

 

 

 

1. HDFS 설계: 대용량 파일 분산 저장 + 순차 읽기 최적화.
2. 블록(Block): 저장 최소 단위 128MB, 복제 → 내고장성/가용성.
3. 네임노드: 메타데이터 관리, 장애 시 HDFS 전체 영향.
4. 데이터노드: 실제 블록 저장, 네임노드에 상태 보고.
5. 클라이언트: open/read/write만 호출 → 내부 처리 자동.
6. 블록 캐싱: 자주 쓰는 데이터 메모리 저장 → 읽기 성능 ↑.
7. HA 구조: Active/Standby + QJM → 네임노드 장애 자동 복구.
8. 쓰기 모델: 블록 단위, 단일 writer, close() 후 전체 파일 가시성.
9. 일관성: "쓰기 완료 후 읽기 가능" → 블록 단위 스트리밍 가능.
10. distcp: 대규모 HDFS 데이터 병렬 복사, 클러스터 균형 유지 가능.