본문으로 건너뛰기

S3 Parquet를 DuckDB로 조회하기

· 약 3분

분석용 데이터를 별도 DB로 옮기지 않고, S3에 Parquet로 쌓아 DuckDB로 직접 SQL 조회하는 방법을 정리합니다.

분석용 데이터는 운영 DB로 옮기지 않아도 됩니다. S3에 Parquet로 쌓아 두고, DuckDB로 그 파일을 직접 SQL 조회합니다.

별도 분석 DB나 무거운 ETL 없이 읽기 전용 분석이 됩니다.

Parquet와 DuckDB, 무엇을 하는 물건인가​

Parquet는 열(컬럼) 단위로 저장하는 파일 형식이고, DuckDB는 파일을 직접 읽어 SQL을 돌리는 가벼운 분석용 DB입니다. 둘을 합치면 S3에 올려 둔 Parquet 파일을 옮기지 않고 그대로 조회할 수 있습니다.

행 단위 저장(CSV 등)이 서류를 통째로 묶어 두는 방식이라면, Parquet는 항목별 칸으로 나눠 정리해 둔 서류함입니다. "이름 칸만 보고 싶다"면 그 칸만 꺼내면 됩니다.

운영 DB에 분석 쿼리를 직접 돌리면​

운영 DB에 분석 쿼리를 직접 돌리면 무거운 집계가 서비스 트래픽과 자원을 다툽니다. 그렇다고 본격 데이터 웨어하우스를 두기엔 무겁고 비쌉니다.

그 중간이 S3 Parquet + DuckDB입니다. 데이터는 S3에 값싸게 쌓아 두고, 분석할 때만 DuckDB가 필요한 부분을 읽습니다. 운영 DB는 건드리지 않고, 상시 떠 있는 분석 서버도 두지 않습니다.

운영 DB에 분석 쿼리를 직접 돌리면 트래픽과 자원을 다투는 경우와, S3 Parquet에 쌓고 DuckDB로 필요할 때만 읽는 구조 비교

S3 Parquet 직접 조회 (httpfs, CREATE SECRET)​

DuckDB에 S3 접근 확장(httpfs)을 올리면, S3 경로를 테이블처럼 바로 조회할 수 있습니다.

INSTALL httpfs; LOAD httpfs;

-- 자격 증명은 SECRET 으로 등록합니다
CREATE OR REPLACE SECRET s3_analytics (
TYPE s3,
PROVIDER config,
KEY_ID '...',
SECRET '...',
REGION 'ap-northeast-2'
);

-- S3 의 Parquet 를 직접 조회
SELECT count(*)
FROM read_parquet('s3://my-bucket/events/2026-06-19/*.parquet');

EC2·EKS 처럼 인스턴스 역할이나 IRSA 로 권한이 붙어 있는 곳에서는 키를 적지 않습니다. aws 확장을 올리고 PROVIDER credential_chain 을 쓰면 AWS SDK 가 알아서 자격을 찾습니다.

INSTALL aws; LOAD aws;
CREATE OR REPLACE SECRET s3_analytics (TYPE s3, PROVIDER credential_chain);

SET s3_region = '...' 로 설정하는 옛 방식도 아직 동작하지만, DuckDB 문서는 이쪽을 Legacy Authentication Scheme 으로 분류하고 secret 사용을 권합니다.

여러 파일은 글롭(*)으로 한 번에 읽습니다. 별도 적재(load) 단계가 없습니다.

Hive 파티셔닝으로 읽는 양 줄이기​

날짜 같은 기준으로 폴더를 나눠 저장하면(파티셔닝) 빨라집니다. 쿼리 조건에 그 기준이 들어가면, DuckDB는 해당 폴더만 읽고 나머지는 건너뜁니다(파티션 프루닝).

-- dt=YYYY-MM-DD 로 폴더가 나뉘어 있으면
SELECT user_id, count(*)
FROM read_parquet('s3://my-bucket/events/*/*.parquet', hive_partitioning = true)
WHERE dt = '2026-06-19' -- 이 폴더만 읽음
GROUP BY user_id;

여기에 Parquet의 컬럼 저장 특성이 더해집니다. SELECT user_id만 하면 그 열만 읽고, 나머지 열은 디스크에서 읽지도 않습니다.

파티션으로 읽을 파일 수를 줄이고, 컬럼으로 파일 안에서 읽는 양을 줄입니다.

파티션 프루닝으로 해당 날짜 폴더만 읽고, 컬럼 저장으로 필요한 열만 읽어 읽는 양을 줄이는 원리

작은 파일이 많아질 때 (ROW_GROUP_SIZE, compaction)​

이벤트를 건건이 작은 파일로 쌓으면 파일 하나하나 여는 비용이 조회 시간을 잡아먹습니다. 주기적으로 묶어 주는 작업(compaction)이 필요합니다.

묶는 단위는 Parquet 의 row group 입니다. DuckDB 의 ROW_GROUP_SIZE 기본값은 122,880행이고, 최솟값은 DuckDB 벡터 크기인 2,048행입니다. 압축은 row group 단위로 걸리기 때문에 크게 잡을수록 압축률이 올라갑니다. 대신 스레드마다 그만큼을 메모리에 들고 있다가 내보내고, row group 은 하나의 파일 안에서도 병렬로 읽히므로 무작정 키우면 병렬성이 줄어듭니다. 이 트레이드오프의 적정값은 데이터 모양에 따라 다르므로 기본값에서 시작해 재 보는 쪽이 낫습니다.

WHERE 에 파티션 기준(dt 등)이 없으면 프루닝이 걸리지 않고 전체를 스캔합니다. 파티션을 나눠 두고도 조건을 안 걸면 나눈 의미가 없습니다.

그리고 이 방식은 읽기 전용 분석에 맞습니다. 잦은 갱신과 트랜잭션이 필요한 데이터는 운영 DB 에 둡니다.

Q&A​

  • 운영 DB 데이터를 어떻게 S3 Parquet로 보내나요?
    • 보통 이벤트나 변경분을 주기적으로 Parquet로 내보내(export) S3에 적재합니다. 실시간이 필요하면 스트리밍으로, 아니면 배치로 충분합니다.
  • DuckDB를 어디서 실행하나요?
    • 임베디드라 분석 코드 안에서 바로 띄웁니다. 상시 서버가 필요 없고, 조회할 때만 프로세스가 S3를 읽습니다.
  • 데이터가 아주 커지면요?
    • 어디까지 버티는지는 데이터 모양과 쿼리 패턴에 달려 있어서 일반적인 기준선을 말하기 어렵습니다. 파티셔닝과 compaction 을 먼저 걸어 보고, 그래도 쿼리 시간이 안 나오거나 동시 사용자가 늘면 그때 데이터 웨어하우스를 검토합니다.

참고자료​