DuckDB는 왜 빠를까?
서버 없이, 클러스터 없이, 파일 위에서 바로 SQL을 실행하는 방식
요즘 데이터 엔지니어링과 분석 워크플로우에서 DuckDB를 자주 보게 됩니다.노트북, ETL 파이프라인, 대시보드, 테스트 환경, SaaS 내부 분석 기능까지 활용 범위도 넓어졌습니다.
흥미로운 점은 DuckDB가 거대한 클러스터를 전제로 하지 않는다는 점입니다.별도 서버를 띄우지 않아도 되고, 복잡한 설정이나 마이그레이션 없이 Parquet, CSV, JSON 같은 파일을 바로 SQL로 조회할 수 있습니다.
그런데 어떻게 이런 방식이 빠르게 동작할 수 있을까요?
Greybeam의 글 「DuckDB Internals: Why is DuckDB Fast?」는 이 질문을 DuckDB 내부 구조 관점에서 설명합니다.
DuckDB는 서버가 아니라 프로세스 안에서 실행됩니다
대부분의 데이터베이스는 서버 구조를 가집니다.클라이언트가 SQL을 보내고, 데이터베이스 서버가 쿼리를 실행한 뒤, 결과를 다시 네트워크로 돌려줍니다.
이 과정에서 생각보다 큰 비용이 발생합니다.
데이터베이스 내부의 결과는 메모리 위의 typed value입니다.하지만 이를 클라이언트로 보내려면 네트워크 전송 가능한 포맷으로 바꿔야 합니다.다시 클라이언트는 이 값을 자기 언어의 타입으로 해석해야 합니다.
즉, 쿼리 실행 자체보다 결과를 주고받는 과정이 더 무거워질 수 있습니다.
DuckDB는 이 구조를 다르게 가져갑니다.별도 서버가 아니라 애플리케이션 안에 라이브러리처럼 로드됩니다.Python 코드에서 DuckDB를 호출하면, 같은 프로세스 안에서 pandas, Arrow, 파일 데이터를 직접 다룰 수 있습니다.
이 구조 덕분에 네트워크 왕복, 직렬화, 역직렬화 비용을 크게 줄일 수 있습니다.
SQL은 바로 실행되지 않습니다
DuckDB에 SQL을 입력하면 곧바로 데이터를 읽는 것이 아닙니다.먼저 SQL 문자열을 엔진이 이해할 수 있는 구조로 바꿉니다.
흐름은 대략 이렇습니다.
SQL 문자열→ 파싱→ 바인딩→ 논리 계획→ 최적화→ 물리 계획→ 실행
파싱 단계에서는 SQL을 AST, 즉 추상 구문 트리로 바꿉니다.바인딩 단계에서는 컬럼명, 테이블명, 함수, 타입을 실제 스키마와 연결합니다.
예를 들어 l_quantity라는 단어는 단순한 문자열이 아니라, 특정 테이블의 특정 타입을 가진 컬럼으로 해석됩니다.
그 다음 옵티마이저가 쿼리를 더 빠르게 실행할 수 있도록 구조를 바꿉니다.
대표적인 예가 filter pushdown입니다.WHERE 조건을 가능한 한 데이터 스캔 단계 가까이 밀어 넣어, 읽지 않아도 되는 데이터를 초기에 제거합니다.
상관 서브쿼리를 조인으로 바꾸거나, 여러 테이블 조인의 순서를 다시 정하는 작업도 이 단계에서 이뤄집니다.
빠른 데이터베이스는 단순히 데이터를 빨리 읽는 것이 아니라, 읽기 전에 “어떻게 덜 읽을지”를 먼저 결정합니다.
실행 계획은 파이프라인으로 나뉩니다
최적화가 끝나면 DuckDB는 논리 계획을 물리 계획으로 바꿉니다.
논리 계획은 “무엇을 할지”를 말합니다.물리 계획은 “어떤 알고리즘과 실행 방식으로 처리할지”를 정합니다.
예를 들어 조인 하나만 봐도 여러 실행 방식이 가능합니다.hash join, index join, merge join 같은 선택지가 있고, DuckDB는 입력 데이터와 조건에 맞춰 적절한 물리 연산자를 고릅니다.
이후 실행은 하나의 거대한 트리 순회가 아니라, 여러 파이프라인으로 나뉩니다.
필터링, projection, hash join의 probe side처럼 한 줄씩 흘려보내며 처리 가능한 연산은 파이프라인 안에서 이어집니다.
반대로 GROUP BY, ORDER BY, hash join의 build side처럼 전체 입력을 어느 정도 모아야 다음 단계로 넘어갈 수 있는 연산도 있습니다.이런 연산은 pipeline breaker, 또는 sink로 동작합니다.
DuckDB는 이 파이프라인 단위로 병렬성을 활용합니다.각 스레드는 자기 몫의 데이터를 처리하고, 필요한 시점에 결과를 합칩니다.
컬럼 기반 저장이 분석 쿼리에 유리합니다
DuckDB는 분석용 데이터베이스입니다.분석 쿼리는 보통 수많은 행을 스캔하고, 일부 컬럼을 필터링하고, 집계하고, 조인합니다.
이런 작업에는 row store보다 column store가 유리합니다.
예를 들어 300개 컬럼이 있는 테이블에서 4개 컬럼만 조회한다고 가정해보겠습니다.
row store라면 전체 레코드를 읽은 뒤 필요 없는 296개 컬럼을 버려야 합니다.column store라면 처음부터 필요한 4개 컬럼만 읽으면 됩니다.
DuckDB는 데이터를 컬럼 단위로 저장하고, row group 단위로 나눕니다.각 row group은 병렬 처리의 단위가 되며, 여러 스레드가 서로 다른 row group을 처리할 수 있습니다.
Zone map은 읽지 않아도 되는 데이터를 건너뜁니다
DuckDB의 저장 구조에서 중요한 개념 중 하나가 zone map입니다.
zone map은 각 row group에 대해 컬럼별 최소값, 최대값, null 개수 같은 정보를 담습니다.
예를 들어 이런 조건이 있다고 해보겠습니다.
WHERE event_date > '2026-01-01'
DuckDB는 실제 데이터를 읽기 전에 row group의 max 값을 확인합니다.어떤 row group의 max event_date가 2026년 1월 1일보다 작거나 같다면, 그 row group은 조건을 만족할 가능성이 없습니다.
그럼 해당 row group은 통째로 건너뜁니다.
이 방식은 Snowflake의 micro-partition pruning, BigQuery의 block pruning과 비슷한 아이디어입니다.이름은 다르지만 핵심은 같습니다.
데이터를 잘 읽는 것보다, 읽지 않아도 되는 데이터를 빠르게 제외하는 구조입니다.
Parquet을 빠르게 읽는 이유
DuckDB가 Parquet 파일을 빠르게 읽는 이유도 같은 맥락에서 이해할 수 있습니다.
Parquet은 DuckDB의 내부 포맷은 아니지만, 구조적으로 분석 쿼리에 잘 맞습니다.
Parquet도 컬럼 기반 포맷이고, row group 단위 통계를 가지고 있습니다.DuckDB는 Parquet 파일의 footer를 읽어 스키마와 row group 통계를 확인합니다.
그 다음 조건을 만족할 수 있는 row group만 고르고, 필요한 컬럼 chunk만 읽습니다.
원격 저장소에 있는 Parquet 파일도 전체를 다운로드하지 않습니다.먼저 footer만 가져와 필요한 byte range를 계산하고, 필요한 부분만 요청합니다.
그래서 조건절이 잘 작동하는 Parquet 쿼리는 로컬뿐 아니라 원격 환경에서도 빠르게 실행될 수 있습니다.
CSV는 반대로 추론이 필요합니다
CSV는 Parquet과 다릅니다.
Parquet은 스키마, 컬럼 구조, 통계를 파일 안에 가지고 있습니다.CSV는 그냥 텍스트입니다.
구분자가 쉼표인지 파이프인지, quote 문자는 무엇인지, 첫 줄이 헤더인지, 각 컬럼 타입이 무엇인지 DuckDB가 직접 추론해야 합니다.
DuckDB는 이를 위해 CSV sniffer를 사용합니다.샘플 데이터를 보고 dialect, 컬럼 타입, 헤더 여부를 자동으로 판단합니다.
이 부분은 DuckDB가 파일을 단순히 읽는 도구가 아니라, 파일을 SQL 데이터베이스처럼 다루기 위해 여러 준비 작업을 수행한다는 점을 보여줍니다.
이 글에서 가져갈 핵심
DuckDB가 빠른 이유는 하나의 마법 같은 기능 때문이 아닙니다.
서버를 거치지 않는 in-process 구조,SQL을 실행 전에 정리하고 최적화하는 과정,컬럼 기반 저장 방식,row group과 zone map을 활용한 pruning,Parquet의 통계를 활용해 필요한 데이터만 읽는 방식이 함께 작동합니다.
이 글을 읽고 나면 DuckDB를 단순한 “가벼운 로컬 DB”로 보기 어렵습니다.
DuckDB는 파일과 메모리 위에 있는 데이터를 분석용 SQL 엔진처럼 다루기 위해 설계된 시스템입니다.작은 도구처럼 보이지만, 내부에는 현대적인 분석 DB의 핵심 아이디어들이 압축되어 있습니다.
데이터를 빠르게 처리한다는 것은 더 많은 컴퓨팅 자원을 쓰는 일이 아닐 수 있습니다.때로는 네트워크를 피하고, 복사를 줄이고, 필요한 컬럼만 읽고, 읽지 않아도 되는 row group을 건너뛰는 설계의 문제입니다.
DuckDB의 속도는 바로 그 설계에서 나옵니다.
[원문]
의견을 남겨주세요