사전 작은 파일 문제
문제

작은 파일 문제

gabury1고친 사람 github-actions[bot]

작은 파일 문제는 데이터를 아주 작은 파일 여럿에 나눠 담아서 처리가 느려지고 관리가 무거워지는 문제입니다. 파일마다 따로 드는 고정 비용이 데이터를 다루는 비용보다 커지기 때문입니다. 많은 데이터를 파일로 쌓아 두고 여러 서버가 나눠 읽는 데이터 처리 시스템에서 주로 부르는 이름입니다.

쉽고 빠른 이해

작은 파일 문제는 같은 데이터를 잘게 쪼개 담아서 일이 느려지는 문제입니다. 1GB 를 파일 하나에 담으면 금방 읽는데, 1KB 짜리 파일 백만 개에 담으면 훨씬 오래 걸립니다.

까닭은 파일마다 드는 고정 비용입니다. 파일을 찾아 열고 머리 정보를 읽는 일은 파일이 작아도 매번 똑같이 듭니다. 파일 하나를 맡아 읽을 작업 단위를 띄우는 비용도 파일마다 붙습니다.

어떻게 생기나:

  1. 데이터를 몇 분마다 조금씩 적재합니다
  2. 적재 한 번에 여러 작업 단위가 저마다 파일을 하나씩 씁니다
  3. 날짜나 지역별 폴더로 잘게 나누면 파일 수가 그만큼 곱해집니다

무엇이 나빠지나:

여러 서버에 나눠 담는 저장소에는 파일 목록만 따로 쥐는 서버가 있습니다. 파일이 많으면 그 서버의 메모리가 찹니다. 읽는 작업은 계산보다 파일 열기와 작업 단위 준비에 시간을 씁니다. 작은 파일을 큰 파일로 합치는 작업을 따로 돌려야 풀립니다.

상세

작은 파일 문제는 파일 하나하나에 붙는 고정 비용이 쌓이는 문제입니다. 같은 양의 데이터인데도 다루는 데 드는 시간과 메모리가 파일 수에 비례해 불어납니다.

이 이름에서 「작다」는 정해진 크기가 아닙니다. 그 시스템이 한 번에 읽기 알맞은 단위보다 한참 작다는 뜻입니다. 분산 데이터 처리에서 그 단위는 대개 수십에서 수백 MB(Megabyte, 메가바이트)입니다.

이삿짐 상자 비유

이삿짐을 큰 상자 열 개에 담으면 열 번 나르면 끝납니다. 같은 짐을 성냥갑 만 개에 나눠 담으면 짐의 무게는 그대로인데 일이 끝나지 않습니다.

성냥갑마다 이름표를 붙여 목록에 적은 뒤 하나씩 들어 옮겨야 하기 때문입니다. 짐을 나르는 시간보다 상자를 다루는 시간이 더 커집니다.

파일마다 드는 고정 비용

고정 비용은 파일 크기와 상관없이 파일 하나마다 매번 드는 일입니다. 1KB 짜리 파일이든 1GB 짜리 파일이든 한 번씩은 꼭 치러야 합니다.

이 비용은 대개 메타데이터를 다루는 데서 생깁니다. 메타데이터는 데이터에 대한 데이터입니다. 파일 이름·크기·위치·만든 시각, 그리고 파일 안의 값이 어떤 모양인지 같은 정보가 여기에 듭니다.

단계 파일마다 하는 일
찾기 파일 목록에서 이름과 위치를 찾는다
열기 저장소에 연결해 파일을 연다
머리 읽기 파일 앞이나 끝에 붙은 메타데이터를 읽는다
일 나누기 그 파일을 맡아 읽을 작업 단위를 만들고 띄운다

표의 네 줄은 파일이 커도 한 번, 작아도 한 번입니다. 그래서 1GB 를 파일 하나로 읽으면 네 줄을 한 번 치릅니다. 1KB 백만 개로 읽으면 백만 번 치릅니다. 정작 읽을 데이터의 양은 두 경우가 같습니다.

파일 수가 불어나는 조건

분산 데이터 처리에서 큰 작업은 여러 서버에서 동시에 도는 작은 작업 단위로 나뉩니다. 이 작업 단위를 태스크라 부릅니다. 데이터를 저장소에 쓸 때도 여러 태스크가 나눠 씁니다.

작은 파일은 대개 누가 일부러 만들지 않습니다. 아래 조건이 겹치면 저절로 쌓입니다.

  1. 적재를 자주 해서 한 번에 들어가는 데이터가 적습니다
  2. 적재 한 번에 여러 태스크가 동시에 저마다 자기 파일을 씁니다
  3. 데이터를 디렉터리로 잘게 나눠서, 한 태스크가 디렉터리마다 파일을 하나씩 씁니다
  4. 이미 쓴 파일에 덧붙이지 못해서 새 데이터는 늘 새 파일로 들어갑니다
  5. 작은 파일을 큰 파일로 합치는 작업이 없습니다

넷째 조건은 한 번 쓰고 닫으면 고칠 수 없는 파일 형식에서 늘 성립합니다. 뒤에 나오는 열 지향 파일이 그렇습니다. 객체를 통째로만 올리는 저장소, 뒤에 나오는 오브젝트 스토리지도 마찬가지입니다.

적재 한 번이 만드는 파일 수는 태스크 수와 디렉터리 수의 곱까지 갑니다. 태스크 둘이 지역 디렉터리 둘에 나눠 쓰면 이렇게 됩니다.

flowchart TD
    L["적재 한 번"] --> T1["태스크 1"]
    L --> T2["태스크 2"]
    subgraph 서울["지역 디렉터리 · 서울"]
        F1["파일 1"]
        F2["파일 2"]
    end
    subgraph 부산["지역 디렉터리 · 부산"]
        F3["파일 3"]
        F4["파일 4"]
    end
    T1 --> F1
    T1 --> F3
    T2 --> F2
    T2 --> F4

태스크마다 디렉터리 하나에 파일 하나씩이니 적재 한 번에 파일이 넷 생깁니다. 여기에 적재 횟수가 다시 곱해집니다.

숫자를 키워 봅니다. 1분마다 적재하고, 한 번에 태스크 50개가 지역 디렉터리 10개에 나눠 쓴다고 해 봅시다. 하루 치 데이터는 10GB 라고 둡니다.

Python
loads_per_day = 24 * 60          # 1440
files_per_load = 50 * 10         # 500
files_per_day = 1440 * 500       # 720000
avg = 10_000_000_000 // 720000   # 13888

하루에 파일이 72만 개 생기고, 파일 하나의 평균 크기는 약 14KB 입니다. 알맞은 단위인 수십 MB 와 견주어도 천 배 넘게 작습니다. 한 달이면 파일이 2천만 개를 넘습니다.

HDFS 네임노드의 메모리

HDFS(Hadoop Distributed File System, 하둡 분산 파일 시스템)는 큰 파일을 여러 서버에 나눠 담는 파일 시스템입니다. 파일을 블록이라는 토막으로 잘라 여러 서버에 흩어 둡니다. 블록은 수십에서 수백 MB 로 크게 잡습니다.

네임노드는 HDFS 의 파일 목록을 쥐는 서버 한 대입니다. 어느 파일이 어떤 블록으로 이루어졌고 블록마다 어느 서버에 있는지를 전부 메모리에 올려 둡니다. 한곳에 다 있으니 찾기가 빠릅니다.

대신 파일 하나, 블록 하나마다 네임노드 메모리를 조금씩 씁니다. 파일이 작아도 한 개는 한 개라서, 작은 파일이 수백만 개면 디스크보다 네임노드 메모리가 먼저 찹니다. 담을 수 있는 파일 수가 네임노드 메모리 크기로 정해집니다.

블록보다 작은 파일이 디스크를 블록 크기만큼 차지하지는 않습니다. HDFS 는 파일의 실제 크기만큼만 디스크에 씁니다. 작은 파일이 낭비하는 것은 디스크가 아니라 네임노드가 쥐는 목록입니다.

처리 엔진의 태스크 수

Apache Spark 나 MapReduce 같은 처리 엔진은 입력을 태스크 몫으로 잘라 태스크에 하나씩 맡깁니다. 이렇게 자른 입력 한 몫을 입력 스플릿이라 부릅니다.

하둡 계열의 입력 방식에서는 입력 스플릿 하나가 두 파일에 걸치지 않습니다. 파일 하나가 아무리 작아도 스플릿 하나를 따로 차지합니다. 태스크 수는 파일 수보다 적어지지 않습니다.

태스크 하나를 띄우는 데도 고정 비용이 듭니다. 태스크를 돌릴 서버를 골라 코드를 보내야 하고, 끝나면 결과를 모아야 합니다. 파일 하나가 14KB 면 계산은 순식간인데 이 준비 시간은 줄지 않습니다. 작업 시간 대부분이 계산 아닌 준비에 쓰입니다.

하둡의 CombineFileInputFormat 이나 Spark 의 파일 읽기처럼 작은 파일 여럿을 태스크 하나에 묶어 맡기는 방식도 있습니다. 그래도 파일마다 목록을 뒤지고 여는 비용은 남습니다. 작업이 시작되기 전에 파일 목록을 다 훑는 것만으로도 오래 걸립니다.

오브젝트 스토리지의 요청 수

오브젝트 스토리지는 파일을 객체라는 단위로 네트워크 요청을 보내 넣고 꺼내는 저장소입니다. 객체는 통째로 올리고 통째로 바꿉니다.

가공하기 전의 데이터를 파일로 한곳에 쌓아 두는 곳을 데이터 레이크라 부릅니다. 클라우드에서는 데이터 레이크를 흔히 오브젝트 스토리지 위에 둡니다.

객체 하나를 읽을 때마다 요청이 하나 나갑니다. 요청마다 네트워크 왕복이 붙어서, 객체가 작을수록 기다리는 시간이 읽는 시간보다 커집니다. 파일 목록도 한 번에 정해진 개수씩 나눠 돌려주므로 객체가 많으면 목록 요청도 여러 번 나갑니다.

Amazon S3 처럼 요청 수에 따라 요금을 매기는 서비스가 많습니다. 그런 곳에서는 작은 파일 문제가 느려짐에 더해 비용으로도 나타납니다.

열 지향 파일의 요약 읽기

열 지향 저장은 값을 행이 아니라 열 단위로 모아 적는 방식입니다. Apache Parquet 가 대표적입니다. 같은 열의 값끼리 모여 있어 압축이 잘 됩니다. 필요한 열만 골라 읽을 수도 있습니다.

이 포맷은 파일 끝에 열마다 값의 범위 같은 요약을 적어 둡니다. 엔진은 이 요약을 보고 필요 없는 구간을 건너뜁니다.

파일이 작으면 이 장점이 줄어듭니다. 열 하나에 모인 값이 적어서 압축할 것도 건너뛸 것도 적습니다. 그런데 파일마다 요약을 따로 읽어야 하니 파일 수만큼 여는 비용만 커집니다.

일반 파일 시스템의 작은 파일

서버 한 대의 디스크를 쓰는 평범한 파일 시스템에도 비슷한 일이 있습니다. 파일마다 아이노드를 하나씩 씁니다. 아이노드는 파일 하나의 메타데이터를 담는 칸입니다. 그 개수는 파일 시스템을 만들 때 정해지는 경우가 많습니다.

그래서 작은 파일이 아주 많으면 디스크에 빈 공간이 남았는데도 아이노드가 먼저 떨어져 새 파일을 못 만듭니다.

파일은 디스크를 블록 단위로 차지합니다. 이 블록은 HDFS 블록과 다른, 대개 몇 KB(Kilobyte, 킬로바이트)의 훨씬 작은 디스크 단위입니다. 블록보다 작은 파일은 블록 하나를 다 차지하고 나머지 공간을 버리기도 합니다.

이 표제어는 대개 앞의 분산 데이터 처리 쪽을 가리킵니다.

알아채는 법

작은 파일 문제는 오류를 내지 않습니다. 작업이 점점 느려지거나 저장소가 무거워지는 것으로만 드러납니다. 숫자 몇 개를 나란히 놓고 봅니다.

아래 표의 테이블은 여러 파일을 한 표처럼 묶어 조회하는 단위입니다. 파일은 여럿이어도 쿼리에서는 테이블 하나로 보입니다.

보는 것 작은 파일 문제일 때
테이블이나 디렉터리의 파일 수 데이터 양보다 훨씬 빨리 늘어난다
평균 파일 크기 수십~수백 MB 보다 한참 작다
태스크 하나가 도는 시간 몇 초도 안 걸리는 태스크가 수만 개다
파일 목록을 쥐는 서버의 메모리 데이터가 별로 안 늘었는데도 찬다

둘째 줄이 가장 빨리 보입니다. 데이터 양을 파일 수로 나누기만 하면 됩니다.

푸는 방법

고치는 길은 파일 수를 줄이는 것 하나입니다. 이미 쌓인 것을 합치거나, 처음부터 덜 만드는 것입니다.

수단 무엇을 바꾸나
컴팩션 쌓인 작은 파일을 읽어 큰 파일로 다시 쓴다
쓰기 전에 태스크 수 줄이기 적재 한 번에 생기는 파일 수를 줄인다
적재 간격 늘리기 한 번에 쓰는 데이터를 키운다
디렉터리를 거칠게 나누기 곱해지는 디렉터리 수를 줄인다
테이블 포맷의 정리 작업 합치기와 옛 파일 지우기를 테이블 단위로 돌린다

마지막 줄의 테이블 포맷은 한 테이블이 어떤 파일들로 이루어졌는지를 따로 기록해 두는 규칙입니다. 그 기록이 있어서 파일을 합치거나 지우는 일을 테이블 단위로 안전하게 돌릴 수 있습니다.

컴팩션은 같은 데이터를 한 번 더 읽고 다시 쓰는 일이라 그만큼 자원을 씁니다. 적재 간격을 늘리면 새 데이터가 그만큼 늦게 보입니다. 어느 쪽을 고르든 무언가를 내줍니다.

반대로 너무 크게 합쳐도 탈이 납니다. gzip 처럼 파일 중간부터 풀 수 없는 압축을 쓰면 여러 태스크가 파일 하나를 나눠 읽지 못합니다. 처음부터 끝까지 태스크 하나가 혼자 읽어야 해서 그 태스크만 오래 돕니다.

관련 항목

작은 파일 문제가 드러나는 저장소

HDFS · 오브젝트 스토리지 · Amazon S3 · 데이터 레이크 · 파일 시스템 · 분산 파일 시스템

작은 파일 때문에 느려지는 처리 엔진

Apache Spark · Hadoop · MapReduce · Apache Hive · Trino · Apache Flink

작은 파일을 만들어 내는 적재 방식

스트림 처리 · 마이크로 배치 · 배치 처리 · 파티셔닝 · 증분 적재 · ETL

파일마다 비용을 물리는 구성 요소

네임노드 · 메타데이터 · 태스크 · 아이노드 · 블록 (파일 시스템) · 입력 스플릿

작은 파일을 합치거나 덜 만드는 수단

컴팩션 · 테이블 포맷 · Apache Iceberg · Delta Lake · Apache Hudi · 하둡 아카이브 · SequenceFile

작은 파일에서 장점이 줄어드는 파일 포맷

Apache Parquet · Apache ORC · 열 지향 저장 · Apache Avro · 압축

작은 파일 문제와 함께 불리는 데이터 처리 장애

데이터 스큐 · 메모리 부족 · 지연 도착 데이터 · 고아 파일 · 스냅샷 만료

작은 파일 문제가 속하는 상위 분류

데이터 엔지니어링 · 빅데이터 · 분산 처리 · 분산 시스템

다른 이름: small files problem · small file problem · 스몰 파일 문제