파티션 쓰기와 오브젝트 스토리지
Hive partitioning, S3 secrets, multipart write, small files 문제를 DuckDB 운영 기준으로 정리합니다.
핵심 요약
- httpfs extension으로 S3 호환 스토리지를 읽고 쓰며, 인증은 DuckDB Secrets Manager(credential_chain, SCOPE)를 표준으로 둡니다.
- persistent secret은 암호화되지 않은 binary로 디스크에 남을 수 있어, 공유·개발 장비에서는 session secret이나 cloud role을 먼저 씁니다.
- hive_partitioning=true와 partition key filter는 파일 skip으로 이어지지만, 파티션을 너무 많이 만들면 파일 수와 object listing 비용이 커집니다.
- PARTITION_BY에는 expression을 직접 넣을 수 없으므로 query 안에서 year/month 파티션 컬럼을 만든 뒤 쓰고, partition당 최소 100MB 이상을 권장합니다.
- COPY는 object storage의 atomic directory publish까지 풀어주지 않으므로, run id staging prefix에 먼저 쓴 뒤 manifest·pointer를 바꿔 노출합니다.
DuckDB는 로컬 파일뿐 아니라 S3 호환 오브젝트 스토리지에 직접 읽고 씁니다. 덕분에 분석 batch가 단순해지지만, DBA 관점에서는 credential, overwrite, small files, partition cardinality를 분명히 챙겨야 합니다.
S3 secret
httpfs extension은 S3 API 기반 스토리지 읽기, 쓰기, globbing을 지원합니다. 인증은 DuckDB Secrets Manager를 표준으로 둡니다.
INSTALL httpfs;
LOAD httpfs;
CREATE OR REPLACE SECRET lake_s3 (
TYPE s3,
PROVIDER credential_chain,
REGION 'ap-northeast-2',
SCOPE 's3://company-lake/'
);
SELECT *
FROM read_parquet('s3://company-lake/events/dt=2026-05-14/*.parquet');persistent secret은 암호화되지 않은 binary format으로 디스크에 남을 수 있습니다. 공유 노트북이나 개발 장비라면 session secret이나 cloud role을 먼저 씁니다.
Hive partitioning 읽기
SELECT *
FROM read_parquet(
's3://company-lake/orders/year=*/month=*/*.parquet',
hive_partitioning = true
)
WHERE year = 2026
AND month = 5;partition key filter는 불필요한 파일을 건너뛰게 해줍니다. 다만 파티션 컬럼을 너무 많이 만들면 파일 수와 object listing 비용이 커집니다.
Partitioned write
COPY (
SELECT
*,
year(order_date) AS year,
month(order_date) AS month
FROM orders
) TO 's3://company-lake/curated/orders'
(FORMAT parquet, PARTITION_BY (year, month), COMPRESSION zstd);PARTITION_BY에는 expression을 직접 넣을 수 없으므로, 위처럼 query 안에서 파티션 컬럼을 만든 뒤 씁니다.
Small files 기준
DuckDB 문서는 작은 partition을 많이 쓰면 비용이 크다고 보고, 보통 partition당 최소 100MB 이상을 권장합니다.
| 증상 | 원인 | 대응 |
|---|---|---|
| 파일 수가 partition마다 thread 수만큼 늘어남 | partitioned write가 directory별로 file을 생성 | threads 조정, 후속 compaction |
| object listing이 느림 | 너무 많은 partition과 file | partition key 줄이기 |
| selective query가 느림 | partition key가 filter와 맞지 않음 | 날짜/tenant 등 실제 필터 기준으로 재설계 |
| overwrite가 위험함 | object storage는 directory transaction이 아님 | staging prefix 후 promote |
안전한 publish 패턴
DuckDB의 COPY는 파일을 잘 써주지만, object storage의 atomic directory publish까지 풀어주지는 않습니다. production에서는 final path에 바로 쓰지 말고,
run id가 붙은 staging prefix에 먼저 쓴 다음 manifest, catalog, pointer를 바꿔서 노출합니다.
2.0 S3 경로 전환
S3 URL 뒤 ?에 credential·region 설정을 붙이던 코드는 secret으로 옮깁니다. 2.0 기본 동작에서는 해당 문자열이 object key 일부로 해석되어 인증 문제처럼 보이는 파일 검색 오류가 날 수 있습니다. URL 목록과 secret의 SCOPE를 함께 검사합니다. S3 migration.
비동기 I/O의 도입은 쓰기 publish의 원자성을 바꾸지 않습니다. staging → 검증 → manifest 전환 절차를 유지합니다.
2.0 PARTITION_PATH와 ORDER_BY
PARTITION_PATH는 파티션별 하위 경로, ORDER_BY는 쓰기 전 정렬을 지정합니다. 아래 로컬 예제는 kr/, us/ 경로를 만듭니다. 실행할 때마다 비어 있는 출력 경로를 사용합니다.
COPY (
SELECT * FROM (VALUES ('KR', 3), ('KR', 1), ('US', 2)) AS v(region, id)
) TO '/tmp/duckdb-partition-demo'
(FORMAT parquet, PARTITION_BY (region),
PARTITION_PATH lower(region), ORDER_BY (id));경로는 비어 있지 않은 상대 경로여야 하며 ..를 포함할 수 없습니다. PARTITION_PATH에는 PARTITION_BY가 필요합니다. 서로 다른 파티션 값이 같은 경로로 합쳐지지 않게 설계하고, region=KR 형태를 벗어나면 기존 Hive partition 자동 추론과 달라진다는 점을 소비자와 합의합니다. ORDER_BY는 조회 결과 순서를 보장하지 않으므로 결과 정렬이 필요하면 SELECT에도 ORDER BY를 둡니다. Partitioned Writes.