분석용 SQL을 돌리려고 서버를 세우고 계정을 만들고 데이터를 적재하는 절차는, 정작 확인하려는 것이 CSV 몇 개나 Parquet 한 디렉터리일 때 배보다 배꼽이 커진다. DuckDB는 이 지점을 겨냥해 만들어진 인프로세스(in-process) 분석 데이터베이스다. 별도 서버 프로세스 없이 애플리케이션과 같은 프로세스 안에서 돌면서, 열 지향 저장과 벡터화 실행으로 집계 질의를 처리한다. 공식 문서는 스스로를 "in-process SQL OLAP database management system"으로 규정하고, 라이선스는 MIT다. 여기서는 이 임베디드 OLAP 엔진이 어떤 원리로 동작하는지, 그리고 무엇을 기준으로 고르거나 걸러야 하는지를 공식 문서에 적힌 사양과 제약을 근거로 정리한다.

DuckDB가 서버 없이 분석을 처리하는 구조

프로세스 안에서 도는 데이터베이스

DuckDB에는 데몬이 없다. 라이브러리를 불러오면 그 자리에서 데이터베이스가 열리고, 질의 결과는 네트워크를 거치지 않고 같은 메모리 공간으로 돌아온다. 흔히 "SQLite의 분석판"이라는 비유가 쓰이는 이유가 이것이다. 설계 목표는 SQLite와 같은 배포 편의성(외부 의존성 없는 C++ 구현, 단일 라이브러리)을 유지하면서 처리 대상을 트랜잭션이 아니라 분석 질의로 바꾸는 것이다. 클라이언트는 Python, R, Java, Node.js, Rust, Go, 명령줄(CLI), 그리고 브라우저에서 도는 WebAssembly 빌드까지 공식으로 제공된다.

열 지향 저장과 벡터화 실행

OLAP 질의는 행 전체가 아니라 몇 개 열만 훑는 경우가 대부분이다. DuckDB는 데이터를 열 단위로 저장해 필요한 열만 읽고, 실행 엔진은 값을 한 건씩 처리하는 대신 고정 크기 벡터(공식 문서 기준 기본 2,048개 값) 묶음으로 처리한다. 튜플 단위 함수 호출 비용이 사라지고 CPU 캐시와 SIMD를 살릴 수 있어, 같은 집계라도 행 지향 엔진과 처리량 차이가 크게 벌어진다. 열 지향 저장은 압축에도 유리해 동일한 데이터가 차지하는 디스크 용량이 줄어든다. 반대로 한 행의 모든 열을 자주 읽고 쓰는 작업에는 이 구조가 손해라는 점도 같은 이유에서 따라온다.

단일 파일 저장소와 ACID 트랜잭션

DuckDB 데이터베이스는 파일 하나다. 복사하면 그것이 백업이고, 옮기면 그것이 이관이다. 파일 경로를 주지 않고 열면 메모리 전용 데이터베이스가 되어 프로세스 종료와 함께 사라진다. 저장 엔진은 MVCC 기반 ACID 트랜잭션을 지원하므로, 중간에 실패한 적재 작업이 테이블을 반쯤 망가진 상태로 남기지 않는다. 임베디드라고 해서 트랜잭션이 없는 도구는 아니라는 뜻이다.

로컬 분석은 어디에서 시작되는가

데이터를 적재하지 않고 파일에 직접 질의한다

로컬 분석에서 가장 큰 마찰은 적재다. DuckDB는 SELECT * FROM 'data/*.parquet'처럼 파일 경로를 테이블 자리에 그대로 쓸 수 있다(replacement scan). CSV는 구분자와 자료형을 추론하는 리더가 내장돼 있고, JSON과 Parquet도 기본 지원 범위에 있다. Parquet의 경우 열 단위 형식이라 필요한 열과 행 그룹만 읽는 프루닝이 동작하므로, 파일을 먼저 데이터베이스에 넣어야 빨라지는 것도 아니다. 탐색 단계에서는 적재 없이 질의하고, 반복해서 쓸 것만 테이블로 굳히는 순서가 자연스럽다.

원격 저장소와 확장 기능

확장(extension)은 필요할 때 설치해 불러오는 구조다. httpfs 확장을 올리면 HTTP(S)와 S3 호환 오브젝트 스토리지의 파일을 같은 문법으로 읽을 수 있고, 이 밖에 전문 검색, 공간 데이터, 다른 데이터베이스 연결용 확장 등이 공식·커뮤니티 저장소로 배포된다. 다만 확장은 배포 환경에서 네트워크로 내려받는 동작을 포함하므로, 오프라인이나 폐쇄망에서는 사전 배치가 필요하다.

기존 분석 도구와의 연결

Python 클라이언트는 같은 프로세스 안의 DataFrame을 별도 변환 없이 질의 대상으로 삼을 수 있고, Apache Arrow와는 메모리 표현을 공유해 복사 비용을 줄이는 경로가 마련돼 있다. 이 특성 때문에 DuckDB는 기존 분석 스택을 갈아엎는 대체재라기보다, SQL이 편한 구간만 넘겨받는 계산 엔진으로 끼워 넣기 쉽다. 로컬 분석 파이프라인에서 "무거운 집계와 조인은 SQL로, 나머지는 원래 쓰던 도구로" 같은 분담이 가능한 이유다.

선택 기준 — 무엇과 비교해서 고르는가

비슷해 보이는 선택지들의 자리

DuckDB를 고를지 말지는 대개 SQLite, PostgreSQL, 클라우드 데이터 웨어하우스 사이의 어디에 두느냐의 문제로 좁혀진다. 판단의 축은 세 가지다. 실행 방식(서버가 필요한가), 데이터 접근 패턴(행 단위인가 열 단위인가), 동시 쓰기 요구(여러 프로세스가 동시에 써야 하는가).

구분DuckDBSQLitePostgreSQL클라우드 DW
실행 방식인프로세스(임베디드)인프로세스(임베디드)서버 프로세스관리형 서비스
저장 지향열 지향행 지향행 지향열 지향
주 대상 작업분석·집계(OLAP)트랜잭션(OLTP)트랜잭션 중심 범용대규모 분석
동시 쓰기파일당 쓰기 프로세스 1개파일 잠금 기반 직렬화다중 클라이언트다중 클라이언트
데이터 위치로컬 파일·원격 파일로컬 파일서버 스토리지서비스 내부
운영 부담없음(라이브러리)없음(라이브러리)설치·백업·튜닝과금·권한 관리

동시성 모델이 사실상 첫 번째 기준

가장 먼저 걸러야 할 조건은 성능이 아니라 동시성이다. 공식 문서 기준으로 하나의 데이터베이스 파일에는 쓰기 권한을 가진 프로세스가 하나만 붙을 수 있다. 같은 프로세스 안에서는 여러 연결과 여러 스레드가 동시에 읽고 쓸 수 있지만, 별개의 프로세스가 같은 파일을 동시에 열어 쓰는 형태는 지원 대상이 아니다(읽기 전용으로 여는 경우에는 다중 프로세스 접근이 가능하다). 웹 애플리케이션 인스턴스 여러 개가 한 파일을 공유하는 구성을 상상하고 있다면, 그 구성은 시작 지점에서 이미 맞지 않는다.

도입 전에 확인해야 할 제약

메모리 한도와 디스크 스필

DuckDB는 메모리보다 큰 데이터도 처리할 수 있도록 조인·정렬·집계 중간 결과를 디스크로 내리는 경로를 갖고 있다. 다만 이것이 무제한을 뜻하지는 않는다. memory_limit(공식 문서 기준 기본값은 시스템 메모리의 약 80%)과 임시 파일이 쌓이는 temp_directory의 여유 공간이 실제 한계를 만든다. 메모리 전용 모드에서는 임시 디렉터리를 따로 지정하지 않으면 디스크로 내릴 곳 자체가 없다는 점도 함께 봐야 한다. 대용량 작업 전에 두 설정값을 확인하는 것이 가장 값싼 사전 점검이다.

저장 포맷과 버전

단일 파일 포맷은 편하지만, 파일을 장기 보관하거나 팀에서 돌려 볼 때는 버전 호환을 확인해야 한다. 공식 문서는 1.0 이후 저장 포맷의 하위 호환(구 버전 파일을 새 버전으로 읽는 방향)을 보장한다고 밝히고 있으나, 상위 호환(새 버전이 만든 파일을 구 버전으로 여는 방향)은 별개 문제다. 장기 보존이나 도구 간 교환이 목적이라면 원본은 Parquet 같은 개방 포맷으로 두고 DuckDB 파일은 작업용으로 쓰는 편이 안전하다.

확장과 자격증명 취급

원격 오브젝트 스토리지를 붙이는 순간 접근 키가 개입한다. DuckDB는 자격증명을 관리하는 시크릿 기능을 제공하지만, 임시 시크릿과 영구 저장 시크릿의 보관 위치가 다르므로 어느 쪽을 쓰는지 확인해야 한다. 커뮤니티 확장은 공식 코어 확장과 신뢰 수준이 같지 않다는 점도 도입 판단에 포함시켜야 한다. 임베디드 OLAP 엔진은 애플리케이션과 같은 프로세스에서 돌기 때문에, 확장이 들여오는 코드도 같은 권한으로 실행된다.

어떤 경우에 적합한가

맞는 조건

다른 선택지를 봐야 하는 조건

정리하면 DuckDB는 규모가 아니라 구조로 선택하는 도구다. "데이터가 크냐"보다 "쓰기 주체가 하나인가", "읽는 방식이 열 단위인가"를 먼저 확인하면 판단이 빨라진다. 로컬 분석의 반복 주기를 줄이는 것이 목적이고 그 조건들이 맞아떨어진다면, 임베디드 OLAP이라는 선택지는 서버 한 대를 아끼는 정도가 아니라 작업 절차 자체를 짧게 만든다. 반대로 조건이 어긋나는데 성능 수치만 보고 들이면, 동시 쓰기 제약이라는 벽에 가장 먼저 부딪힌다.