파일과 Block
우리가 일반적으로 사용하는 컴퓨터의 경우, 하나의 파일은 여러개의 블록으로 저장되고, 블록의 크기는 대부분 (운영체제나 설정에 따라 다를수 있지만) 512bytes 이다. 예를 들어 1MB의 파일을 저장한다고 하면 약 20개의 512 bytes의 블록에 나뉘어 저장되는 것이다.
이 블록크기가 너무 작아지면, 블록의 갯수가 많아져 메타데이터 오버헤드가 증가하게 되고, 블록의 크기가 너무 커지게 되면 공간 낭비의 위험이 증가합니다. 예를 들어 블록크기가 128MB라고 한다면, 1MB 짜리 파일을 저장하든, 5MB파일을 저장하든 상관없이 128MB를 블록을 할당해야하기 때문에 그 파일을 저장한 공간 외의 다른 공간을 사용하지 못합니다.
Block File System 의 장점
-
간결한 메타데이터: 각 블록의 크기가 고정되어 있기 때문에 파일 메타데이터에서 각 블록에 대한 위치와 크기를 명시적으로 저장할 필요가 없습니다. 이로 인해 메타데이터의 복잡성이 감소합니다.
-
효율적인 디스크 사용: 블록을 고정된 크기로 나누면 디스크의 공간 배치를 최적화하고, 디스크의 빈 공간을 최소화할 수 있습니다. 이로 인해 디스크의 사용률이 향상됩니다.
-
빠른 임의 접근: 블록 기반의 접근 방식은 임의의 위치에서 데이터에 접근하는 것을 용이하게 합니다. 특정 블록을 찾기 위해 필요한 계산이 간단하므로, 임의 접근 성능이 향상됩니다.
-
강력한 내결함성: 일부 블록 파일 시스템은 추가적인 오류 검출 및 복구 메커니즘이 블록 수준에서 제공됩니다. 이러한 메커니즘은 각 블록의 무결성을 확보하고, 오류가 발생할 경우 해당 블록만 복구하도록 도와줍니다.
-
효율적인 캐시 및 버퍼 관리: 블록을 고정된 크기로 관리하면, 시스템의 캐시와 버퍼 관리를 최적화할 수 있습니다. 이는 데이터 읽기 및 쓰기 작업의 성능을 향상시킵니다.
-
파일 크기의 동적 확장: 블록 기반의 파일 시스템은 필요에 따라 추가적인 블록을 할당하거나 반환하여 파일의 크기를 동적으로 확장하거나 축소할 수 있습니다.
-
데이터 조각화 최소화: 파일을 일정한 크기의 블록으로 나누면, 디스크상에서 파일의 조각화를 줄일 수 있습니다. 따라서 연속적인 디스크 영역에 데이터를 저장하고 읽어올 수 있어 성능이 향상됩니다.
HDFS 블록 사이즈 128MB
HDFS(Hadoop File System)는 default로 128MB라는 훨씬 큰 크기의 블록사이즈를 가집니다. 이는 하둡이 태생적으로 대용량 데이터를 처리하도록 설계되었기 때문에 Random 처리보다는 스트리밍 처리를 위해 최적화 되었기 때문입니다. 또한 블록크기가 커지면 그만큼 Meta데이터의 크기도 작아지고, 이는 네임노드의 오버헤드를 감소시키기 때문에 관리측면에서 유리합니다.
Hadoop 네임노드의 경우 일반적으로 100만개의 블록을 저장할 때 1GB의 Heap Memory를 사용한다고 합니다.

128MB는 어디서 오는가: dfs.blocksize
아래 내용은 Apache Hadoop 3.3.5 기준입니다. hadoop.apache.org의 stable 문서 세트가 가리키는 버전이며, 기본값은 메이저/마이너 버전마다 바뀔 수 있으므로 사용 중인 버전의 hdfs-default.xml을 직접 확인하는 것이 가장 정확합니다.
128MB라는 숫자는 코드에 박혀 있는 상수가 아니라 설정 항목입니다. hdfs-default.xml에 이렇게 정의되어 있습니다.
<!-- Apache Hadoop 3.3.5 / hdfs-default.xml -->
<property>
<name>dfs.blocksize</name>
<value>134217728</value>
<description>The default block size for new files, in bytes.</description>
</property>
<property>
<name>dfs.namenode.fs-limits.min-block-size</name>
<value>1048576</value>
<description>Minimum block size in bytes, enforced by the Namenode at create time.</description>
</property>
<property>
<name>dfs.namenode.fs-limits.max-blocks-per-file</name>
<value>10000</value>
<description>Maximum number of blocks per file, enforced by the Namenode on write.</description>
</property>
세 값이 함께 있어야 그림이 완성됩니다.
첫째, dfs.blocksize의 기본값은 134217728바이트, 즉 정확히 128MB입니다. 이름 그대로 "새 파일의 기본 블록 크기"이지 강제값이 아닙니다.
둘째, dfs.namenode.fs-limits.min-block-size가 1048576바이트, 즉 1MB로 걸려 있습니다. 네임노드가 파일 생성 시점에 강제하는 하한선입니다. 블록 크기를 512KB로 낮춰서 작은 파일 문제를 우회하려는 시도가 실패하는 이유가 이것입니다. 네임노드가 아예 거부합니다. 정말로 낮추려면 이 값 자체를 먼저 낮춰야 하는데, 그렇게 하면 뒤에서 설명할 네임노드 메모리 문제로 그대로 걸어 들어가게 됩니다.
셋째, dfs.namenode.fs-limits.max-blocks-per-file이 10000으로 걸려 있습니다. 파일 하나가 가질 수 있는 블록 수의 상한입니다. 기본 블록 크기 128MB와 곱하면 파일 하나의 실질 상한이 약 1.25TB가 됩니다. 아주 큰 단일 파일을 다루면서 블록 크기를 줄이면 이 한도에 먼저 부딪히게 됩니다.
클러스터 전체가 아니라 파일 단위로 바꾸기
dfs.blocksize는 클러스터 기본값이지만, 쓰기 시점에 파일 단위로 지정할 수도 있습니다. Hadoop의 명령들은 공통적으로 제네릭 옵션 -D <property>=<value>를 받습니다.
# 클러스터 기본값(128MB)으로 쓰기
hdfs dfs -put ./big.parquet /user/hadoop/
# 이 파일만 256MB 블록으로 쓰기
hdfs dfs -D dfs.blocksize=268435456 -put ./huge.parquet /user/hadoop/
# 이 파일만 64MB 블록으로 쓰기 (min-block-size 1MB 이상이므로 허용됨)
hdfs dfs -D dfs.blocksize=67108864 -put ./medium.parquet /user/hadoop/
# 참고: hadoop fs 와 hdfs dfs 는 같은 파일시스템 셸이다.
# 문서상 put 의 형식은 다음과 같다.
# hadoop fs -put [-f] [-p] [-l] [-d] [-t <thread count>] [-q <thread pool queue size>] [ - | <localsrc> ...] <dst>
여기서 중요한 성질이 하나 있습니다. 블록 크기는 쓰기 시점에 파일에 고정되고, 이미 쓴 파일의 블록 크기는 나중에 설정을 바꿔도 변하지 않습니다. 클러스터 기본값을 128MB에서 256MB로 올려도 어제 쓴 파일은 여전히 128MB 블록으로 남아 있습니다. 기존 파일의 블록 크기를 바꾸려면 새 설정으로 다시 써야 합니다. 그래서 "블록 크기를 잘못 잡았다"는 문제는 설정 변경이 아니라 재작성 작업이 됩니다.
블록 크기를 키우는 것이 항상 이득도 아닙니다. 40GB 파일을 512MB 블록으로 쓰면 블록이 80개뿐이라, 코어가 수백 개인 클러스터에서 대부분의 코어가 놀게 됩니다. 블록 크기는 네임노드 부담과 병렬도 사이의 타협입니다.
네임노드 메모리 이야기, 조금 더 정확하게
위에서 "100만 블록당 1GB 힙"이라는 경험칙을 적었습니다. 방향은 맞지만, 실제로 네임노드 힙을 소모하는 대상이 무엇인지를 알면 훨씬 쓸모 있는 판단을 할 수 있습니다.
HDFS 아키텍처 문서는 네임노드의 역할을 이렇게 설명합니다. 네임노드는 블록 복제에 관한 모든 결정을 내리고, 각 데이터노드로부터 주기적으로 하트비트와 블록 리포트를 받습니다. 하트비트는 데이터노드가 정상 동작 중이라는 뜻이고, 블록 리포트는 그 데이터노드가 가진 모든 블록의 목록입니다. 즉 네임노드는 파일시스템의 이름 공간 전체와 블록 위치 정보를 메모리에 들고 있는 존재입니다.
그래서 힙을 먹는 것은 데이터의 총 바이트 수가 아니라 세 가지 객체의 개수입니다.
- 파일(inode) 개수
- 디렉터리 개수
- 블록 개수
여기서 작은 파일 문제(small files problem)의 정체가 드러납니다. 1MB짜리 파일 100만 개와 128GB짜리 파일 8개는 데이터 총량이 비슷하지만 네임노드가 관리해야 하는 객체 수는 자릿수가 다릅니다.
같은 데이터 총량, 다른 네임노드 부담
=========================================
(A) 1MB 파일 1,000,000 개
파일 객체 1,000,000
블록 객체 1,000,000 (파일마다 최소 1블록)
합계 2,000,000 개 이상의 객체를 네임노드가 메모리에 유지
(B) 128GB 파일 8 개
파일 객체 8
블록 객체 8,192 (128GB / 128MB = 1,024 블록/파일)
합계 약 8,200 개
데이터 총량은 (A) 약 1TB, (B) 1TB 로 비슷하다.
네임노드 부담은 두 자릿수 이상 차이가 난다.
부수적으로:
- 맵 태스크도 대개 블록 단위로 생성되므로 (A) 는 태스크가 100만 개
- 태스크 하나당 기동 비용이 실제 계산보다 커진다
블록 크기가 클수록 유리하다는 원래 글의 논지가 여기서 정량적으로 확인됩니다. 파일이 작으면 블록 크기를 키워도 소용이 없다는 점도 같이 확인됩니다. 1MB 파일은 블록 크기가 128MB든 512MB든 블록 1개를 차지하고, 실제 디스크는 1MB만 씁니다. HDFS 블록은 로컬 파일시스템의 고정 크기 블록과 달리 사용하지 않는 공간을 미리 잡아 두지 않기 때문입니다. 즉 작은 파일이 낭비하는 것은 디스크가 아니라 네임노드 메모리와 태스크 슬롯입니다.
실제로 확인하는 법
# 클러스터 전체 요약: 용량, 데이터노드 상태, 저복제/손상/누락 블록 수
hdfs dfsadmin -report
# 특정 상태의 데이터노드만
hdfs dfsadmin -report -live
hdfs dfsadmin -report -dead
# 이름 공간 전체를 걸어 파일/블록 수를 세기 (부하가 크므로 한산할 때)
hdfs fsck / -files -blocks
# 디렉터리별 파일 수와 디렉터리 수 (작은 파일이 어디에 쌓이는지 찾을 때)
hdfs dfs -count -v /user/hadoop
예시 출력: hdfs dfsadmin -report (앞부분)
============================================
Configured Capacity: 6597069766656 (6.00 TB)
Present Capacity: 6543210987654 (5.95 TB)
DFS Remaining: 4102410240000 (3.73 TB)
DFS Used: 2440800747654 (2.22 TB)
DFS Used%: 37.30%
Replicated Blocks:
Under replicated blocks: 0
Blocks with corrupt replicas: 0
Missing blocks: 0
Missing blocks (with replication factor 1): 0
Low redundancy blocks with highest priority to recover: 0
Pending deletion blocks: 0
-------------------------------------------------
Live datanodes (6):
Name: 10.0.1.31:9866 (dn1.example.com)
Hostname: dn1.example.com
Rack: /rack1
Decommission Status : Normal
Configured Capacity: 1099511627776 (1.00 TB)
DFS Used: 406800124609 (378.86 GB)
DFS Remaining: 684121568575 (637.14 GB)
DFS Used%: 37.00%
Last contact: Sat Aug 16 10:14:02 KST 2026
Num of Blocks: 3121
읽는 법:
Num of Blocks 를 전체 데이터노드에 대해 더하고 복제 계수로 나누면
논리 블록 수의 대략적인 규모가 나온다.
이 값이 수천만 단위로 올라가면 네임노드 힙을 먼저 의심한다.
출력 항목은 버전에 따라 다르므로 사용 중인 버전의 문서에서 확인하세요.
처음부터 끝까지: 파일 하나가 블록으로 쪼개지는 과정
파일을 하나 올리고, 그것이 어떻게 쪼개졌으며 어느 데이터노드에 놓였는지를 직접 확인해 보겠습니다. 307.2MB짜리 파일을 기본 설정(블록 128MB, 복제 3)으로 올린다고 하겠습니다.
# 1) 파일을 HDFS 로 올린다
hdfs dfs -put ./big.parquet /user/hadoop/big.parquet
# 2) 파일이 몇 개의 블록으로 쪼개졌고 각 블록이 어디에 있는지 본다
# 문서상 형식:
# hdfs fsck <path> [-files [-blocks [-locations | -racks | -replicaDetails]]]
hdfs fsck /user/hadoop/big.parquet -files -blocks -locations
# 3) 블록 크기만 빠르게 확인하고 싶다면
hdfs dfs -stat "%o %r %b" /user/hadoop/big.parquet
예시 출력: hdfs fsck ... -files -blocks -locations
=====================================================
FSCK started by hadoop (auth:SIMPLE) from /10.0.1.21
for path /user/hadoop/big.parquet at Sat Aug 16 10:12:44 KST 2026
/user/hadoop/big.parquet 322122547 bytes, replicated: replication=3, 3 block(s): OK
0. BP-1093428495-10.0.1.10-1690000000000:blk_1073745821_5001 len=134217728 Live_repl=3
[DatanodeInfoWithStorage[10.0.1.31:9866,DS-9f1c3a1e,DISK],
DatanodeInfoWithStorage[10.0.1.42:9866,DS-2a77b904,DISK],
DatanodeInfoWithStorage[10.0.1.55:9866,DS-c4108d55,DISK]]
1. BP-1093428495-10.0.1.10-1690000000000:blk_1073745822_5002 len=134217728 Live_repl=3
[DatanodeInfoWithStorage[10.0.1.32:9866,DS-71ba0cd2,DISK],
DatanodeInfoWithStorage[10.0.1.42:9866,DS-2a77b904,DISK],
DatanodeInfoWithStorage[10.0.1.56:9866,DS-88e2f013,DISK]]
2. BP-1093428495-10.0.1.10-1690000000000:blk_1073745823_5003 len=53687091 Live_repl=3
[DatanodeInfoWithStorage[10.0.1.31:9866,DS-9f1c3a1e,DISK],
DatanodeInfoWithStorage[10.0.1.43:9866,DS-1d6e4477,DISK],
DatanodeInfoWithStorage[10.0.1.56:9866,DS-88e2f013,DISK]]
Status: HEALTHY
Number of data-nodes: 6
Number of racks: 2
Total dirs: 0
Total symlinks: 0
Replicated Blocks:
Total size: 322122547 B
Total files: 1
Total blocks (validated): 3 (avg. block size 107374182 B)
Minimally replicated blocks: 3 (100.0 %)
Over-replicated blocks: 0 (0.0 %)
Under-replicated blocks: 0 (0.0 %)
Mis-replicated blocks: 0 (0.0 %)
Default replication factor: 3
Average block replication: 3.0
Missing blocks: 0
Corrupt blocks: 0
이 출력에서 읽어야 할 것은 다음과 같습니다.
블록이 3개로 쪼개졌고, 앞의 두 개는 len=134217728, 즉 정확히 128MB입니다. 마지막 블록은 len=53687091, 약 51.2MB입니다. 마지막 블록은 남은 만큼만 차지하며 128MB를 낭비하지 않습니다. 이것이 앞서 말한 "HDFS 블록은 미리 공간을 잡지 않는다"의 실제 증거입니다. 로컬 파일시스템의 고정 블록과 HDFS 블록의 결정적 차이가 이 한 줄에 있습니다.
blk_1073745821_5001 형식은 블록 ID와 생성 스탬프입니다. 앞의 BP- 접두어는 블록 풀 ID로, 어느 네임스페이스에 속한 블록인지를 나타냅니다. Live_repl=3은 지금 살아 있는 복제본 수이고, 대괄호 안의 목록이 실제로 그 블록을 들고 있는 데이터노드입니다.
복제본이 흩어진 모양도 볼 만합니다. 블록 0과 블록 1의 데이터노드 조합이 서로 다릅니다. 덕분에 이 파일을 읽는 잡은 세 블록을 서로 다른 노드에서 병렬로 읽을 수 있습니다. -locations 대신 -racks를 주면 각 복제본이 어느 랙에 있는지까지 나옵니다.
복제와 랙 인식
블록이 어디에 놓이는지는 두 개의 설정과 하나의 정책이 결정합니다.
<!-- Apache Hadoop 3.3.5 / hdfs-default.xml -->
<property>
<name>dfs.replication</name>
<value>3</value>
<description>Default block replication. The actual number of replications
can be specified when the file is created.</description>
</property>
<property>
<name>dfs.namenode.replication.min</name>
<value>1</value>
<description>Minimal block replication.</description>
</property>
<property>
<name>dfs.replication.max</name>
<value>512</value>
<description>Maximal block replication.</description>
</property>
dfs.replication의 기본값은 3입니다. 그리고 dfs.blocksize와 마찬가지로 파일 생성 시점에 파일 단위로 지정할 수 있습니다. 이미 존재하는 파일의 복제 계수는 hadoop fs -setrep [-R] [-w] <numReplicas> <path> 형식의 명령으로 바꿀 수 있습니다.
복제본 3개를 어디에 두는지는 기본 블록 배치 정책이 정합니다. HDFS 아키텍처 문서의 설명은 이렇습니다. 복제 계수가 3인 일반적인 경우, 쓰기 클라이언트가 데이터노드 위에 있으면 첫 복제본을 그 로컬 머신에 두고, 아니면 같은 랙의 임의의 데이터노드에 둡니다. 두 번째 복제본은 다른(원격) 랙의 노드에, 세 번째 복제본은 그 원격 랙의 또 다른 노드에 둡니다.
이 배치가 왜 이런 모양인지는 두 가지 실패를 동시에 막으려는 타협입니다. 세 복제본을 모두 다른 랙에 흩으면 랙 전체가 죽어도 안전하지만, 쓰기 때마다 랙 간 네트워크를 세 번 건너야 해서 느립니다. 반대로 셋 다 같은 랙에 두면 쓰기는 빠르지만 랙 스위치나 전원이 죽으면 블록이 통째로 사라집니다. 2대 1로 나누면 랙 간 트래픽은 한 번으로 줄이면서 랙 단위 장애에도 살아남습니다.
저복제 상태는 fsck 출력에서 바로 보입니다.
예시 출력: 저복제와 누락 블록
================================
# 1) 복제본이 목표치보다 적은 경우 (데이터는 살아 있다)
/user/hadoop/small/part-00007 4096 bytes, replicated: replication=3, 1 block(s):
Under replicated BP-1093428495-10.0.1.10-1690000000000:blk_1073746109_5289.
Target Replicas is 3 but found 2 live replica(s), 0 decommissioned replica(s),
0 decommissioning replica(s).
Under-replicated blocks: 1 (0.0032 %)
Status: HEALTHY
# 2) 복제본이 하나도 남지 않은 경우 (데이터가 사라졌다)
/user/hadoop/raw/2026-08-15.log: MISSING 1 blocks of total size 134217728 B.
Missing blocks: 1
Corrupt blocks: 0
Status: CORRUPT
구분:
Under-replicated --> 아직 정상. 네임노드가 알아서 복제본을 다시 만든다.
데이터노드 이탈/디스크 교체 직후에 흔히 보이며,
시간이 지나면 저절로 0으로 돌아간다.
Missing / Corrupt --> 복구 불가 상태. 사람이 개입해야 한다.
Status 가 CORRUPT 로 바뀐다.
Under-replicated는 경보가 아니라 진행 상황 표시에 가깝습니다. 데이터노드 한 대를 재부팅하거나 디스크를 교체하면 반드시 나타나고, 네임노드가 하트비트와 블록 리포트를 받아 상황을 파악한 뒤 스스로 복제본을 다시 만듭니다. 문제는 이 값이 줄지 않고 정체할 때입니다. 남은 노드의 용량이 부족하거나, 랙이 하나뿐이라 정책을 만족하는 위치가 없거나, 복제 대역폭 제한에 걸린 경우입니다.
실패 사례와 함정
증상: 네임노드 GC가 길어지고 클러스터 전체가 멈춘 것처럼 보인다
작은 파일 문제의 전형적인 최종 형태입니다. 데이터노드는 멀쩡하고 디스크도 남아 있는데 네임노드 응답이 느려집니다.
진단 순서
===========
1) 객체 수를 먼저 센다
hdfs dfsadmin -report 로 전체 블록 규모를 본다.
hdfs fsck / -files -blocks 로 Total files / Total blocks 를 본다.
블록 수가 수천만 단위면 여기서 이미 답이 나온다.
2) 어디에 쌓였는지 찾는다
hdfs dfs -count -v <경로> 를 상위 디렉터리부터 내려가며 실행한다.
대개 스트리밍 잡의 출력 디렉터리나 시간 단위 파티션이 범인이다.
(예: 1분마다 작은 파일 하나씩 쓰는 잡)
3) 네임노드 힙과 GC 로그를 본다
힙 사용량이 상한 근처에 붙어 있고 풀 GC 가 반복되는지 확인한다.
힙을 늘리는 것은 시간을 버는 대응이지 해결이 아니다.
4) 원인을 고친다
- 작은 파일을 주기적으로 합치는 컴팩션 잡을 넣는다
- 스트리밍 잡의 출력 주기를 늘린다 (1분 -> 15분)
- 컬럼형 포맷(Parquet/ORC)으로 묶어 파일 수 자체를 줄인다
- 아카이브가 목적이라면 HAR 로 묶는다
핵심은 순서입니다. 힙을 먼저 늘리면 증상은 가라앉지만 파일 수는 계속 늘어나므로 몇 달 뒤에 더 큰 힙으로 같은 문제를 다시 만나게 됩니다. 객체 수를 먼저 세고, 어디서 늘어나는지를 찾고, 생산자를 고치는 순서여야 합니다.
증상: 잡의 맵 태스크가 비정상적으로 많다
맵 태스크 수는 대체로 입력 스플릿 수를 따라가고, 입력 스플릿은 대체로 블록을 따라갑니다. 그래서 태스크가 수만 개로 튀면 두 가지 중 하나입니다. 입력 파일이 잘게 쪼개져 있거나, 블록 크기를 필요 이상으로 작게 잡았거나.
진단은 입력 경로에 hdfs fsck <경로> -files -blocks를 돌려 파일 수와 블록당 크기를 보는 것부터 시작합니다. 파일 하나가 블록 하나도 채우지 못하고 있다면 파일 수가 문제이고, 파일은 큰데 블록이 잘게 나뉘어 있다면 그 데이터를 쓸 때의 dfs.blocksize 설정이 문제입니다. 후자는 앞서 말했듯 설정만 바꿔서는 고쳐지지 않고 재작성이 필요합니다. 태스크 기동의 고정 비용이 실제 계산 시간보다 커지면, 클러스터는 계산이 아니라 스케줄링에 시간을 씁니다.
증상: fsck에 MISSING 또는 CORRUPT가 보인다
가장 급한 상황이므로 순서를 지키는 것이 중요합니다.
먼저 hdfs fsck / -list-corruptfileblocks로 영향을 받은 파일 목록을 확보합니다. 그 다음 hdfs dfsadmin -report -dead로 죽은 데이터노드가 있는지 확인합니다. 노드가 통째로 빠져 있고 그 노드가 되살아날 수 있는 상태라면, 노드를 복구하는 것이 가장 빠른 해결입니다. 성급하게 -delete를 쓰면 되살릴 수 있었던 블록까지 지웁니다.
노드가 돌아올 수 없다면 그 데이터의 원본이 어디에 있는지를 따집니다. HDFS는 원본 저장소가 아니라 파생 저장소인 경우가 많으므로, 상류 시스템에서 다시 적재하는 것이 정답인 경우가 흔합니다. 마지막으로, 복구가 불가능하고 파일을 정리해야 하는 단계에서만 -move 또는 -delete를 씁니다. -move는 손상된 파일을 lost+found로 옮기고, -delete는 지웁니다.
함정: 복제 계수를 1로 낮춰 용량을 아낀다
용량이 부족할 때 가장 먼저 떠오르는 대응이지만 대가가 큽니다. 복제 계수가 1이면 데이터노드 한 대가 죽는 순간 그 노드에 있던 블록이 곧바로 MISSING이 됩니다. 복구할 다른 복제본이 없기 때문입니다. 게다가 복제본이 하나뿐이면 잡이 데이터 지역성을 활용할 여지도 사라져서, 읽기 성능도 같이 떨어집니다. 정말로 재생성이 자유로운 중간 산출물에만 제한적으로 쓸 수 있는 선택입니다.
언제 HDFS를 쓰지 않나
HDFS 아키텍처 문서는 스스로의 전제를 분명히 밝힙니다. HDFS 애플리케이션은 파일에 대해 한 번 쓰고 여러 번 읽는(write-once-read-many) 접근 모델을 필요로 하며, HDFS는 사용자와의 대화형 사용보다 배치 처리를 위해 설계되었고, 낮은 지연 시간보다 높은 처리량에 방점이 있습니다. 이 전제에서 벗어나는 세 가지 경우에는 다른 저장소가 맞습니다.
첫째, 작은 파일이 아주 많은 워크로드입니다. 이유는 앞에서 다 설명했습니다. 네임노드 메모리가 파일과 블록의 개수에 비례해서 소모되고, 태스크 수도 같이 폭발합니다. 로그를 잘게 쓰는 파이프라인이라면 컬럼형 포맷으로 묶어 파일 수를 줄이거나, 애초에 오브젝트 스토리지나 로그 전용 저장소를 쓰는 편이 낫습니다.
둘째, 낮은 지연 시간의 임의 읽기가 필요한 워크로드입니다. HDFS의 읽기는 네임노드에 블록 위치를 묻고 데이터노드에서 스트리밍으로 읽어 오는 구조라서, 밀리초 단위로 특정 레코드 하나를 꺼내는 데는 맞지 않습니다. 키로 단건을 빠르게 조회해야 한다면 HBase처럼 HDFS 위에 랜덤 접근 계층을 얹은 시스템이나, 아예 다른 종류의 데이터베이스가 필요합니다.
셋째, 제자리 수정(update-in-place)이 필요한 워크로드입니다. HDFS는 추가(append) 지향이며 파일 중간을 임의로 고쳐 쓰는 것을 전제하지 않습니다. 레코드를 자주 갱신하거나 삭제해야 한다면 파일을 통째로 다시 쓰는 수밖에 없습니다. 이 패턴이 필요하면 Iceberg나 Hudi, Delta Lake 같은 테이블 포맷으로 갱신과 삭제를 표현하거나, 트랜잭션을 지원하는 데이터베이스를 쓰는 것이 맞습니다.
반대로 HDFS가 여전히 잘 맞는 자리는 분명합니다. 파일이 크고, 접근 패턴이 순차 스캔이며, 한 번 쓰고 여러 번 읽고, 처리량이 지연 시간보다 중요한 경우입니다. 128MB라는 블록 크기는 바로 그 전제 위에서 고른 숫자입니다.
참고 자료
- Apache Hadoop 3.3.5, hdfs-default.xml (
dfs.blocksize,dfs.replication,dfs.namenode.fs-limits.min-block-size,dfs.namenode.fs-limits.max-blocks-per-file,dfs.replication.max,dfs.namenode.replication.min의 기본값 출처) — https://hadoop.apache.org/docs/stable/hadoop-project-dist/hadoop-hdfs/hdfs-default.xml (2026-08-16 확인) - Apache Hadoop 3.3.5, HDFS Architecture (블록 크기, 복제본 배치 정책, 하트비트/블록 리포트, write-once-read-many) — https://hadoop.apache.org/docs/stable/hadoop-project-dist/hadoop-hdfs/HdfsDesign.html (2026-08-16 확인)
- Apache Hadoop 3.3.5, HDFS Commands Guide (
hdfs fsck,hdfs dfsadmin -report의 형식과 옵션) — https://hadoop.apache.org/docs/stable/hadoop-project-dist/hadoop-hdfs/HDFSCommands.html (2026-08-16 확인) - Apache Hadoop 3.3.5, File System Shell (
put,setrep,stat,count의 형식) — https://hadoop.apache.org/docs/stable/hadoop-project-dist/hadoop-common/FileSystemShell.html (2026-08-16 확인) - Apache Hadoop 3.3.5, Hadoop Commands Guide (제네릭 옵션
-D <property>=<value>) — https://hadoop.apache.org/docs/stable/hadoop-project-dist/hadoop-common/CommandsManual.html (2026-08-16 확인)
퀴즈
Q1: 128MB는 어디에 정의되어 있고, 파일마다 다르게 지정할 수 있나요?
hdfs-default.xml의 dfs.blocksize이며 기본값은 134217728바이트입니다. 쓰기 시점에 파일 단위로 지정할 수
있지만, 그때 파일에 고정되므로 이미 쓴 파일은 다시 써야 바뀝니다. 하한은 1MB로 강제됩니다.
Q2: 네임노드 힙을 소모하는 것은 무엇이고, 왜 작은 파일이 문제인가요?
총 바이트 수가 아니라 메모리에 유지하는 파일 수와 디렉터리 수와 블록 수입니다. 작은 파일은 디스크가 아니라
네임노드 메모리와 태스크 슬롯을 낭비합니다.
Q3: 파일이 몇 개의 블록으로 쪼개졌고 어느 데이터노드에 있는지 어떻게 보나요?
fsck에 -files -blocks -locations를 함께 줍니다. 블록 ID, len, Live_repl, 데이터노드 목록이 나옵니다.
마지막 블록의 len이 작은 이유는 HDFS 블록이 남은 만큼만 차지하기 때문입니다.
Q4: 복제 계수 3일 때 기본 배치 정책은 복제본을 어디에 두나요?
첫 복제본은 쓰기 클라이언트의 로컬 노드 또는 같은 랙의 임의 노드, 두 번째는 다른 랙, 세 번째는 그 원격 랙의
또 다른 노드입니다. 랙 간 네트워크를 한 번만 건너면서 랙 장애에서 살아남는 타협입니다.