650GB 데이터(S3의 Delta Lake). Polars vs. DuckDB vs. Daft vs. Spark
(dataengineeringcentral.substack.com)- 650GB 규모의 Delta Lake 데이터를 S3에 저장하고, 이를 단일 노드 환경에서 Polars, DuckDB, Daft, Spark로 처리한 성능 비교 실험
- 32GB 메모리의 EC2 인스턴스에서 각 엔진이 대용량 데이터를 처리할 수 있는지 검증, 클러스터 기반 Spark 대비 단일 노드의 가능성 탐색
- DuckDB는 16분, Polars는 12분, Daft는 50분, PySpark는 1시간 이상 소요되어, 단일 노드에서도 실질적 처리 가능성 확인
- Polars는 Deletion Vector 미지원, DuckDB만 해당 기능을 지원해 Lake House 호환성에서 차이 존재
- 결과적으로 단일 노드 프레임워크가 저비용 하드웨어에서도 대규모 데이터 처리 가능성을 입증, 분산 컴퓨팅 의존도 재검토 필요성 제기
클러스터 피로와 단일 노드 대안
- SaaS 기반 Lake House 클러스터 운영의 비용과 복잡성이 커지며 ‘클러스터 피로(cluster fatigue)’ 현상 언급
- 과거에는 Pandas 외 대안이 없어 Spark를 사용했으나, DuckDB·Polars·Daft(D.P.D.) 등장으로 단일 노드 처리 가능성 확대
- D.P.D.는 메모리보다 큰(LTM) 데이터셋 처리와 고속 연산이 가능함
- 글은 분산형과 비분산형 두 선택지를 제시하며, “Single Node Rebellion” 개념을 강조
실험 환경 구성
- S3에 Delta Lake 테이블 생성, 약 650GB 데이터를 저장 (1TB 목표였으나 중단)
- EC2 인스턴스(32GB RAM, 16 CPU) 에서 DuckDB, Polars, Daft 실행 후 Spark와 비교
- 데이터는 소셜 미디어 게시물 형태의 모의 데이터로 Python dict를 생성해 Daft DataFrame으로 변환 후 Parquet 파일로 저장
- 이후 Parquet 파일을 Databricks에서 Delta Lake 테이블로 변환, 연·월 단위 파티션 구성
- Delta 로그 제외 시 약 650GB 데이터 확인
메모리 제약과 스트리밍 필요성
- 단일 노드(32GB 메모리)에서 650GB 데이터를 처리해야 하므로 스트리밍 방식 쿼리 실행 필요성 제기
- Polars GitHub 이슈를 인용하며, Iceberg로 스트리밍 쓰기 기능을 요구하는 사례 언급
- Polars·DuckDB 등 신형 프레임워크가 Lake House 포맷을 스트리밍 방식으로 읽고 쓸 수 있는 기본 지원 필요성 강조
각 엔진별 테스트 결과
-
DuckDB
- Deletion Vector 지원 유일
- 650GB 데이터를 32GB 리눅스 머신에서 16분 만에 처리 성공
- 코드 단순하고 결과 파일 정상 생성
-
Polars
- Deletion Vector 미지원으로 Lake House 환경에서 제약 존재
- Lazy API(Scan/Sink) 사용 필요
- 12분 만에 처리 완료, DuckDB보다 빠름
-
Daft
- Rust 기반, 사용감 우수하나 처리 시간 50분으로 가장 느림
- Iceberg 관련 작업에서 안정적 동작 확인
-
PySpark (Databricks Single Node)
- 1시간 이상 소요, 튜닝 없이 실행
- 단일 노드 엔진 대비 효율 낮음
- 실험 목적은 속도보다 단일 노드의 실현 가능성 검증
결론 및 시사점
- 단일 노드 프레임워크가 대용량 Lake House 데이터 처리 가능함을 실험으로 입증
- 저비용 하드웨어에서도 합리적 실행 시간과 간단한 코드 구조 확보
- DuckDB·Polars·Daft 모두 분산 클러스터 없이도 실용적 성능 제공
- 분산 컴퓨팅만이 유일한 해법이 아님을 보여주며, 현대 Lake House 아키텍처의 재고 필요성 제시
- “Single Node Rebellion” 개념을 통해 비용 효율적 데이터 엔지니어링 접근 가능성 부각
Hacker News 의견
- 나는 Eventual에서 일하는 소프트웨어 엔지니어로, 우리 팀이 만든 Daft의 벤치마크를 공유해준 것에 감사함을 전하고 싶음
Daft는 AI 워크로드를 위한 고성능 데이터 처리 엔진으로, 단일 노드와 분산 환경 모두에서 작동함
이번 벤치마크를 통해 병렬성과 파이프라이닝 개선 여지를 많이 발견했음. 특히 deltalake 리더와 groupby 연산자에서 최적화할 부분이 많았음
향후 릴리스에서 이 개선사항을 반영할 예정이며, 자세한 내용은 GitHub, Twitter, LinkedIn에서 확인 가능함
Daft가 흥미롭다면pip install daft로 직접 사용해볼 수 있음- Daft를 ibis의 백엔드로 노출할 계획이 있는지 궁금함. 그렇게 되면 다른 엔진에서 부드럽게 전환하며 테스트하기 좋을 것 같음
- 이건 회사 홍보용으로 만든 계정처럼 보임
- Awk? 관련된 흥미로운 글이 있음 — Command-line tools can be 235x faster than your Hadoop cluster
- 650GB라니, 내 휴대폰에도 들어갈 정도의 작은 데이터임
과도한 툴링 대신 그냥 GNU 도구를 쓰면 됨
참고로 예전 글이지만 여전히 흥미로운 자료임 — command-line tools can be 235x faster than your Hadoop cluster- 이제는 그 글이 다루던 2014년 Hadoop 시대와는 다름
650GB의 JSON 데이터를 CLI 도구로 집계해보면 DuckDB나 ClickHouse의 병렬 처리 성능을 따라가기 어려움. GNU Parallel로도 시도해봤지만 한계가 있었음 - 만약 650TB라면 이야기가 완전히 달라짐. 그 글은 마이크로벤치마크에 불과함
실제로는 데이터 카탈로그와 클러스터 기반의 작업이 필요함 - “그렇게 작은 숫자는 세는 법을 잊었다”는 농담과 함께 이 영상을 공유함
- 이제는 그 글이 다루던 2014년 Hadoop 시대와는 다름
- 나는 종종 DuckDB로 단일 노드에서 ‘biggish data’를 처리함
Delta나 Iceberg 대신 Parquet 파일을 직접 순회하며 쿼리함
BigQuery에서 쿼리한 결과를 로컬 Parquet 파일(약 1GB 단위)로 내려받아 DuckDB로 분석함. RAM보다 훨씬 큰 데이터지만 잘 작동함
BigQuery와 DuckDB 각각의 집계 성능 차이를 비교해, 작업을 두 엔진으로 나눠 실행하기도 함. 이런 조합이 데이터 엔지니어링의 재미있는 부분임- 650GB 정도면 로컬 파일시스템에서도 충분히 처리 가능함. 복잡한 툴이 필요하지 않음
- 이번 벤치마크는 NIC 대역폭에 완전히 지배된 실험처럼 보임
c5.4xlarge 인스턴스의 최대 10Gbps 대역폭으로는 S3에서 650GB를 읽는 데 최소 9분이 걸림
I/O 스케줄링 방식의 미세한 차이가 결과에 큰 영향을 줬을 것 같음
오히려 큰 인스턴스를 써서 빨리 끝내는 게 더 경제적일 수도 있음- 일반 데스크톱이나 괜찮은 노트북에서도 테스트해보면 재미있을 것 같음
NVMe 스토리지는 S3보다 훨씬 빠르고, 로컬 8~16코어 CPU가 클라우드보다 나을 수도 있음
S3는 훌륭한 제품이지만 로컬 스토리지 성능에는 미치지 못함 - 실제 쿼리는 전체 파일을 스캔하지 않고, S3 바이트 범위 읽기를 활용해 일부 컬럼만 처리했을 가능성이 높음
파일 크기 분포나 API 호출 스큐가 더 큰 변수였을 것 같음
“큰 인스턴스가 더 싸게 끝날 수도 있다”는 말에는 전적으로 동의함 - 실험의 가치가 불분명함
Spark는 다단계 대규모 데이터셋에 적합하며, S3를 백엔드로 쓸 때는 네트워크 병목이 비용으로 드러남
DuckDB/Polars의 단일 노드 성능은 인상적이지만, 이는 마치 활주로 위의 비행기와 오토바이를 경주시키는 것과 같음 - 10Gbps라니 너무 낮음. Google에서는 400Gbps NIC과 개선된 TCP 혼잡 제어를 사용했음
이런 차이 때문에 분산 컴퓨팅에 피로감을 느끼는 사람도 많음 - 이 지적에 공감함. 30년 전 월가에서 배운 교훈이 있음 — 시스템 성능을 테스트하기 전에 이론적 최대치를 먼저 이해하라는 것임
자원 한계를 파악하고 그 한계 대비 실제 성능을 비율로 표현하면 훨씬 명확해짐
- 일반 데스크톱이나 괜찮은 노트북에서도 테스트해보면 재미있을 것 같음
- 이 글은 두 가지 측면에서 잘못 표현된 기사임
- 실제로는 컬럼 프루닝이 적용되어 2개 컬럼 + 메타데이터만 접근했을 가능성이 높음
- 대부분의 시간은 S3 I/O에 묶였을 것이며, 동시 연결 제한이 더 큰 영향을 줬을 것임
다양한 시스템을 시도한 점은 좋지만, 메모리보다 큰 쿼리를 본격적으로 다뤘으면 좋겠음
- 쿼리가 전체 650GB 중 일부만 반환하는 프로젝션이라는 점이 중요함
DuckDB는 메모리 초과 스트리밍에 강하지만 Polars는 아직 미성숙함
S3의 기본 설정은 병렬 읽기를 막지 않으므로, 결국 VM의 네트워크 대역폭이 병목일 가능성이 큼
- 최근 몇 TB의 JSON 데이터를 처리해야 했는데, 수많은 10~20MB 크기의 작은 파일이 문제였음
ClickHouse가 가장 빨랐고, DuckDB는 단순성과 안정성 면에서 최고였음
Flink와 PySpark는 3~5배 느렸고, Dask와 Ray도 너무 느림
지금은 대부분의 워크로드에서 DuckDB나 ClickHouse로 시작하길 추천함. Pandas가 느릴 때 DuckDB로 대체하는 게 내 기본 전략임- JSON 데이터를 먼저 다른 포맷으로 변환했는지, 아니면 직접 JSON 위에서 작업했는지 궁금함
- Polars는 Delta Lake 지원을 위해 delta-rs에 의존하며, 이 구현이 Deletion vectors를 지원하지 않음
단일 노드 라이브러리로도 1TB 정도는 충분히 처리 가능하고, 10TB 이상부터 Spark로 넘어가면 됨
관련 이슈- 많은 사람들이 “Spark는 병렬화가 쉽다”는 이유로 너무 빨리 Spark로 넘어감
하지만 더 나은 도구로 해결 가능한 경우가 많음
예전에 주니어 엔지니어가 5GB짜리 JSON 수백 개를 Python 문자열 결합으로 처리하느라 18시간 걸렸는데,
간단한 콘솔 툴과 multiprocessing으로 바꾸니 35분으로 줄었음
적절한 도구 선택이 핵심임
- 많은 사람들이 “Spark는 병렬화가 쉽다”는 이유로 너무 빨리 Spark로 넘어감
- Presto(AWS Athena)가 더 빠르고 나은 대안일 수도 있음. 650GB 데이터를 로컬에서도 테스트해보고 싶음
- Presto는 이제 Trino로 이름이 바뀜
유지보수와 실행 비용이 매우 저렴하고, 가성비 좋은 도구임
- Presto는 이제 Trino로 이름이 바뀜
- DuckDB의 새로운 DuckLake 카탈로그 포맷도 테스트 후보로 좋을 듯함 — ducklake.select
- DuckLake의 데이터 인라인 플러시 기능이 아직 알파 단계임
작은 배치 쓰기 시 Parquet 파일이 너무 많아지는 문제를 해결하려고, DuckLake는 이를 DBMS(Postgres 등)에 인라인 저장함
최근에야 Parquet으로 다시 쓰는 기능이 생겼지만, 아직은 안정화가 필요함
관련 문서 - DuckLake 포맷에는 SQL 의존성이라는 닭-달걀 문제가 있음
카탈로그를 SQL DB로 표현해야 하는데, Parquet의 장점은 바로 그 복잡함을 피하는 데 있음
카탈로그도 Parquet 기반으로 만들면 자가 부트스트랩형 포맷이 될 수 있을 것 같음
- DuckLake의 데이터 인라인 플러시 기능이 아직 알파 단계임