HDFS보다 MinIO가 빠른 진짜 이유 — 접근 경로의 아키텍처
같은 플래시·같은 네트워크·같은 Iceberg인데 왜 Trino+MinIO가 Impala+HDFS보다 빠를까? '매체가 빨라서'가 아니라 스토리지 접근 경로의 구조 차이를 7가지로 분해합니다.
최근 여러 고객사에서 비슷한 보고가 올라옵니다. "HDFS + Impala보다 Trino + MinIO(S3 호환 오브젝트 스토리지) 조합이 더 빠르다" 는 것입니다. 테이블 포맷은 양쪽 다 Iceberg로 동일하게 맞췄는데도요.
직관과 어긋나는 이야기입니다. HDFS는 빅데이터 시대 내내 "데이터 근처에서 연산한다(data locality)"는 무기로 성능을 설명해 왔으니까요. 그런데 왜 이제 와서 오브젝트 스토리지가 더 빠를까요?
이 글은 흔한 설명("디스크가 빨라서", "로컬리티 덕분에")을 의도적으로 전부 제거한 뒤, 순수하게 남는 한 가지 — 스토리지에 접근하는 경로(access path)의 아키텍처 차이 — 만으로 이 현상을 분해합니다.
먼저 변수를 통제하자
엔지니어링에서 "A가 B보다 빠르다"는 말은 무엇을 같게 두고 무엇만 바꿨는지를 명시하지 않으면 의미가 없습니다. 이 비교에서 흔히 섞여 들어오는 교란변수를 먼저 정리합니다.
| 항목 | 이번 분석에서의 전제 |
|---|---|
| 저장 매체 | 양쪽 모두 플래시(NVMe/SSD). "디스크가 빨라서"는 제거. |
| 데이터 접근 | 양쪽 모두 네트워크 경유. HDFS도 분리형(disaggregated) 배포라 로컬 디스크 직접 읽기가 아님. "로컬리티 덕분에"도 제거. |
| 테이블 포맷 | 양쪽 모두 Iceberg. 커밋 의미론·rename 비용 같은 포맷 변수 제거. |
| 남는 변수 | 스토리지 접근 경로(HDFS 블록 프로토콜 vs S3 오브젝트 프로토콜)와 쿼리 엔진(Impala vs Trino). |
핵심: 하드웨어와 로컬리티를 같게 두면, "매체가 빨라서"라는 답은 사라집니다. 그러면 남는 진짜 원인은 데이터를 한 번 읽기 위해 어떤 경로를 거치느냐입니다.
읽기 한 번의 해부 — 2-hop vs 1-hop
같은 Parquet 파일 하나를 여는 동작을 두 경로로 나란히 놓고 보면 차이가 선명해집니다.
HDFS에서는 첫 바이트를 받기 전에 NameNode를 반드시 한 번 거칩니다. 파일을 열 때마다, 스캔 대상 파일 수만큼 이 메타데이터 RPC가 발생합니다. S3/MinIO는 오브젝트의 위치 메타데이터가 분산돼 있어 바이트 레인지 요청 한 방으로 끝납니다.
플래시·네트워크가 같아도 HDFS는 파일당 왕복이 한 번 더 많습니다. 그리고 컬럼 포맷 분석은 "파일 풋터를 읽고 → 필요한 컬럼 청크로 시크" 하는 수많은 작은 읽기의 연속이라, 이 차이가 쿼리 한 번에 수천 번씩 누적됩니다.
동일 하드웨어에서 빨라지는 7가지 원인
1. NameNode = 중앙 집중 메타데이터 + 글로벌 락
HDFS 메타데이터는 단일 액티브 NameNode(JVM 프로세스) 에 모이고, FSNamesystem의 글로벌 락으로 직렬화됩니다. 동시 사용자가 늘면(인터랙티브 BI, 수십 개 동시 쿼리) NameNode의 RPC 큐가 병목이 되고, 큰 힙에서 발생하는 GC 일시정지가 p99 지연 스파이크로 그대로 튑니다.
오브젝트 스토리지에는 이 단일 직렬화 지점이 구조적으로 존재하지 않습니다. 메타데이터가 노드 전체에 샤딩되어 수평 확장됩니다. 같은 하드웨어에서 격차가 가장 크게 벌어지는 지점이 바로 여기이며, 동시성이 높을수록 더 벌어집니다.
2. Impala가 내는 "로컬리티 세금"
Impala는 로컬리티 인식(locality-aware) 엔진이라, 스캔 작업을 데이터가 있는 노드에 배치하려고 NameNode에 블록 위치를 조회합니다. 그런데 스토리지가 분리된(disaggregated) 환경에서는 그 위치 정보로 얻을 이득이 사실상 0입니다 — 어차피 모든 데이터를 네트워크로 읽으니까요.
즉 Impala는 이득이 없는 NameNode 조회를 매번 추가로 발생시키고, 그 부하가 다시 1번 항목(NameNode 병목)을 키웁니다. 자신이 가장 잘 쓰던 무기(로컬리티)가 무력화된 정도가 아니라, 그 무기를 휘두르려는 시도 자체가 비용이 되는 상황입니다. Trino는 로컬리티를 아예 시도하지 않고 split을 라운드로빈으로 분배하므로 이 세금을 내지 않습니다.
3. 3중 복제 핫스팟 vs 이레이저 코딩 스트라이핑
클래식 HDFS는 한 블록을 3개 DataNode에 통째로 복제하고, 읽을 때는 복제본 하나(= DataNode 한 대) 에서 읽습니다. 따라서 단일 블록 읽기의 대역폭이 DataNode 한 대의 디스크·NIC로 제한됩니다.
MinIO는 오브젝트를 이레이저 코딩으로 여러 드라이브·노드에 샤드로 쪼개 저장하므로, 하나의 오브젝트를 읽을 때 여러 디바이스에서 병렬로 데이터가 흘러나옵니다. 같은 플래시라도 단일 객체 처리량의 병렬도가 다릅니다.
HDFS도 Hadoop 3부터 이레이저 코딩 모드를 지원하지만, 핫 데이터에는 잘 쓰지 않고 재구성 읽기(reconstruction read) 비용이 별도로 듭니다. 대부분의 운영 클러스터는 여전히 3중 복제입니다.
4. 작은 파일 / 메타데이터 확장성
HDFS는 파일·블록 하나당 NameNode 힙을 약 150바이트씩 점유합니다. 작은 파일이 수백만 개로 늘면 NameNode 메모리와 리스팅이 무너집니다. 오브젝트 스토리지에서 작은 오브젝트는 분산 인덱스의 키-값 항목일 뿐이라 중앙 힙 압박이 없습니다.
Iceberg가 디렉터리 LIST 대신 매니페스트에서 파일 목록을 가져오므로 양쪽 다 리스팅 폭주는 완화됩니다. 하지만 파일을 실제로 여는 open/stat은 여전히 HDFS에서는 NameNode를 칩니다. 작은 파일이 많을수록 1번의 병목으로 되돌아옵니다.
5. 컬럼 포맷에 딱 맞는 Range GET I/O 스택
Trino의 네이티브 S3 파일시스템 같은 최신 커넥터는 비동기·병렬 레인지 GET, 인접한 작은 읽기 병합(coalescing), 프리페치, 풋터·메타데이터 캐시가 깊게 최적화돼 있습니다. Parquet의 "풋터 읽기 → 여러 컬럼 청크 병렬 읽기" 패턴이 HTTP Range GET과 1:1로 맞아떨어집니다.
HDFS의 DFSClient는 블록 추상화와 NameNode 의존 위에서 동작하기 때문에, 이런 대규모 병렬 레인지 읽기 패턴을 최적화할 여지가 상대적으로 좁습니다.
6. 쿼리 플래닝 단계의 왕복 차이
split을 만들려면 파일 크기·위치 정보가 필요합니다. Iceberg 매니페스트는 파일 크기와 통계를 이미 담고 있어서, S3 경로에서는 플래닝에 필요한 스토리지 왕복이 거의 0입니다.
반면 Impala는 (2번에서 본 것처럼) 로컬리티 스케줄링을 위해 플래닝 도중에도 NameNode 블록 위치 조회를 하는 경향이 있습니다. 그 결과 플래닝 지연이 늘고 NameNode 부하가 커집니다 — 쿼리가 짧을수록 이 고정 비용의 비중이 커집니다.
7. 핫 파일 동시 접근 시 직렬화
인기 있는 같은 파일(예: 작은 차원 테이블, 자주 조인되는 파일)을 여러 쿼리가 동시에 열면, HDFS에서는 그 블록을 가진 소수의 DataNode와 NameNode 락으로 트래픽이 수렴해 핫스팟이 생깁니다. 오브젝트 스토리지는 샤드가 여러 노드에 흩어져 있고 중앙 메타데이터 락이 없어 동시 읽기가 더 평탄하게 분산됩니다.
정직하게 짚을 한 가지 — 엔진 교란변수
위 7가지를 출처별로 다시 분류하면 이렇습니다.
| 원인 | 성격 |
|---|---|
| 1 · 3 · 4 · 7 | 순수 스토리지 계층 (HDFS 아키텍처 vs 오브젝트 스토리지 아키텍처) |
| 2 · 6 | 엔진이 스토리지를 쓰는 방식 (Impala의 로컬리티 설계) |
| 5 | 커넥터 I/O 스택 (Trino의 네이티브 S3 파일시스템 성숙도) |
즉 "Trino + MinIO가 빠르다"는 보고에는 여전히 Impala vs Trino라는 엔진 변수가 섞여 있습니다. 정직한 결론을 내리려면 이 점을 숨기면 안 됩니다.
스토리지 효과만 깨끗이 분리하고 싶다면, 이상적인 통제 실험은 엔진을 Trino로 고정하고 스토리지만 바꾸는 것입니다.
이렇게 측정하면 1·3·4·7번(순수 스토리지)이 실제로 얼마나 기여하는지, 그리고 나머지가 엔진 몫이었는지가 깨끗하게 드러납니다. 고객 환경에서 이 데이터를 뽑을 수 있다면 "왜 빨라졌나"에 대한 답의 설득력이 결정적으로 올라갑니다.
그럼 HDFS는 언제 불리하지 않은가
오해를 막기 위해 균형을 잡자면, 위 분석은 분리형 플래시 + Iceberg 분석 쿼리라는 조건에서의 이야기입니다. 다음과 같은 경우에는 격차가 줄거나 역전될 수 있습니다.
- per-request 레이턴시가 지배적인 초소형 읽기: 오브젝트당 HTTP 왕복 오버헤드가 누적되는 워크로드.
- 로컬리티가 진짜로 살아 있는 코로케이션 배포: compute와 HDFS DataNode를 같은 노드에 올리고 short-circuit local read가 동작하는 전통적 구성.
- 메타데이터 부담이 작은 소수의 거대 파일 순차 스캔: NameNode 왕복 비중이 미미해지는 경우.
따라서 결론은 "HDFS는 죽었다"가 아니라, "분리형·플래시·고동시성 분석 환경에서는 HDFS 접근 경로의 레거시 비용이 더 이상 값을 못 한다" 입니다.
결론
같은 플래시, 같은 네트워크, 같은 Iceberg라면 — 오브젝트 스토리지가 빠른 이유는 매체가 빨라서가 아닙니다. 접근 경로의 아키텍처 때문입니다.
- (a) 객체 스토리지는 중앙 NameNode 메타데이터/글로벌 락 병목을 없애고,
- (b) 컬럼 분석의 Range-GET I/O 패턴에 구조적으로 들어맞으며,
- (c) 단일 객체를 더 넓게 병렬화합니다.
반대로 HDFS 경로는 분리형 플래시 환경에서 아무 이득도 주지 못하는 레거시 비용 — NameNode 홉, 로컬리티 세금, 복제 핫스팟 — 을 그대로 짊어집니다. 여기에 Trino의 성숙한 Iceberg·S3 커넥터가 더해지면서 체감 격차가 벌어집니다.
다음에 "오브젝트 스토리지가 HDFS보다 빠르다"는 보고를 받으면, 매체나 로컬리티를 따지기 전에 "읽기 한 번에 NameNode를 몇 번 거치는가" 를 먼저 물어보세요. 대부분의 답이 거기에 있습니다.