A Preview of DuckDB v2.0

Author: Mark Raasveldt, Hannes Mühleisen | Source: duckdb.org | Published: 2026-08-17


한 줄 요약

“작년이 레이크하우스의 해였다면 이번 릴리스는 DuckDB as a server의 해를 연다” — in-process DB로 시작한 DuckDB가 client/server 모드, 트리거, 비동기 I/O, 새 파서·스토리지 포맷과 함께 장기 실행 서비스로 나아간다.

핵심 주장/내용

  • ① DuckDB as a Server — Quack과 CONNECT: 1일차부터 in-process였지만 client/server 모드 요청이 아주 집요해서 결국 굽혔다. quack 확장이 다른 DuckDB와 대화하는 네이티브 프로토콜을 구현하고 v2.0에서 stable로 졸업한다. 어떤 DuckDB 프로세스든 자기 DB를 네트워크로 서빙할 수 있고, 다른 DuckDB가 붙어서 새 CONNECT 문으로 쿼리를 그리로 라우팅할 수 있다
    • CONNECT는 Quack에 한정되지 않는다 — 그것을 지원하는 어떤 원격 DB로도 세션을 향하게 하고, 새 remote pushdown optimizer가 테이블을 wire로 끌어오는 대신 SQL을 PostgreSQL·MySQL로 직접 보낸다
    • 분석 시스템 경험이 있으면 DuckDB가 트랜잭션 워크로드를 다룰 수 없다고 가정할 수 있지만, DuckDB는 1일차부터 full MVCC와 트랜잭션 격리를 갖춘 트랜잭셔널 다중 연결 DB로 만들어졌다. 대부분의 사용자가 단일 사용자 시나리오에서 그것이 필요 없었을 뿐이다. 트랜잭션을 잘 처리하고 꽤 많은 워크로드에서 PostgreSQL 같은 범용 DB와 경쟁할 만큼 빠르며, client/server 패턴이 마침내 그 기계장치를 멀티테넌트·장기 실행 배포에서 빛나게 한다
    • 장기 운영은 새 도전을 동반하므로 v2.0이 지표·로그·관측성을 밀어붙인다. 프리뷰 몇 주 만에 사람들이 Quack 프로토콜의 독립 클라이언트를 만들었다 — “우리는 DuckDB가 다른 DuckDB와 대화하도록 확장한다고 생각했는데 세상은 아니라고 하고 자기 클라이언트를 만들었다”
  • ② VARIANT가 1급 시민으로: v1.5에 나온 VARIANT는 JSON on steroids로 생각하면 된다. JSON이 빠르다면 어떨까를 상상해보라. JSON처럼 컬럼이 row마다 다른 모양의 데이터를 저장할 수 있지만, JSON과 달리 텍스트 포맷이 아니다 — DuckDB가 반정형 데이터에 숨은 공통 구조를 자동 탐지해 “shred”하므로 스토리지에서 잘 압축되고 쿼리에서 빠르게 실행되며, 스키마를 선언할 필요가 전혀 없다. 구조를 공유하되 시간에 따라 진화하는 JSON스러운 레코드 스트림의 실시간 로그 인제스션에 자연스러운 fit
    • v2.0에서 파이프라인이 end to end로 작동한다 — 스토리지에서 직접 shredded 실행, 스캔으로의 추출 pushdown, Parquet의 shredded VARIANT 읽기·쓰기, variant_* 함수 계열
    • 장기적으로 v2.0 직후쯤 일반 JSON 타입을 VARIANT로 뒷받침할 계획이라 기존 JSON 워크로드가 쿼리 한 줄 바꾸지 않고 이 이점을 다 얻는다
  • ③ 트리거: 오래된 요청 기능을 full로 제공 — BEFORE/AFTER, FOR EACH ROW/FOR EACH STATEMENT, REFERENCING OLD/NEW TABLE을 통한 transition table, 이벤트당 다중 트리거, 트리거된 테이블의 RETURNING, DROP TRIGGER. 고전적 유스케이스는 audit 테이블. 장기 실행 DuckDB 서비스와 자연스럽게 맞고 내부적으로 여러 예정 기능을 만드는 데도 쓸 계획
  • ④ SQL dialect 추가: NEAREST 조인으로 top-k 유사도 검색이 조인 절이 되어 벡터·임베딩 워크로드에 편리 (INNER JOIN products t APPROX NEAREST 2 BY SIMILARITY array_cosine_similarity(...)), CTE 안의 DML(INSERT/UPDATE/DELETE/COPY를 파이프라인 단계로), 중첩 스키마(스키마 안의 스키마), 새 변수 문법(표현식이 허용되는 어디서나 $x), JSON mutation 함수(json_set·json_insert·json_replace·json_remove로 마침내 문서를 in-place 수정), USING KEY 집계를 가진 recursive CTE(순수 SQL에서 반복 알고리즘)
  • ⑤ 비동기 I/O: S3 같은 오브젝트 스토어와의 상호작용이 DuckDB 경험의 중심인데 DuckDB는 오래전부터 병렬로 읽을 수 있었지만 동기 접근이 얼마나 빨라질 수 있는지에 한계를 두었다. v2.0이 엔진 전반에 비동기 I/O를 도입해 I/O 레이어가 쿼리 처리 레이어와 독립적으로 확장되고, 원격 읽기의 병렬성이 훨씬 커져 네트워크 스토리지 쿼리가 극적으로 빨라진다. Parquet이 먼저, CSV와 DuckDB 자체 포맷이 뒤따르고, 비동기 Parquet 쓰기와 새 MMAP·DIRECT_IO 모드도. 로컬 스토리지도 조금 이득이지만 네트워크 스토리지가 큰 이득을 보는 곳
  • ⑥ 전반적으로 빨라진 쿼리: 부분 집계가 조인 아래로 push되고, 중복 집계가 재사용되고, recursive CTE 엔진이 재작성되고, 집계가 메모리를 넘으면 디스크로 spill하고, Windows CLI가 멀티스레드 결과 materialization에서 약 2.2배 빨라졌다
    • row-group pruning이 대폭 확장: min-max index(zone map)와 Parquet Bloom filter가 이제 struct, list, decimal, UUID, IN 필터, 심지어 함수 술어에 대해서도 데이터를 건너뛴다 (WHERE contains(message, 'ERROR'), WHERE substr(code, 1, 3) = 'NL-', WHERE id IN (1, 5, 9) 가 이제 스캔 대신 row group을 프루닝한다)
    • 쿼리 플래닝이 partition-aware가 된다: 레이크하우스 포맷(DuckLake, Iceberg, S3의 평범한 Hive 파티션 Parquet)이 모두 파티션되어 있고 그 파티셔닝을 활용하는 것이 데이터셋을 스캔하느냐 대부분을 건너뛰느냐의 차이인 경우가 많다. v2.0에서 플래너와 옵티마이저가 기존 파티셔닝을 완전히 활용하고 파티션 쓰기도 재작업되었다
  • ⑦ 스토리지 포맷 v2.0: 기본 스토리지 포맷 버전을 v2.0.0으로 올린다. 컬럼 메타데이터가 lazy하게 로드되어 wide 테이블이 더 빨리 열리고, DICT_FSST 문자열 압축이 기본 활성화되고, delete가 compact하게 저장되고, 스토리지 레이어가 읽기 시 훨씬 강한 손상 검증을 한다. 요약하면 큰 인덱스와 wide 테이블을 가진 DB가 더 빨리 열리고 메모리를 훨씬 적게 쓴다. 새 포맷은 checkpoint vacuuming이 row ID가 바뀌는 엔트리를 증분적으로 remapping해 ART 인덱스가 있는 테이블을 compact하게 해서 full 인덱스 재구축을 피한다
    • 올해 후반 ART 인덱스가 buffer-managed되어 메모리 압박 하에 evict될 수 있고 ART 인덱스가 완전히 메모리에 들어가야 하는 제약이 제거된다
  • ⑧ 완전히 새로운 SQL 파서: DuckDB는 유명하게도 항상 PostgreSQL 유래 파서를 썼는데 v2.0이 자체 현대적·확장 가능 PEG 기반 파서를 제공한다. 확장 생태계와 연결되어 확장이 문법 자체에 hook할 수 있으므로 완전히 새로운 SQL 문법을 노출하는 확장을 기대하라. 정확한 소스 위치와 함께 더 나은 에러 메시지도. 옛 것과 호환되게 설계했으므로 파서 교체를 실제로 눈치채지 못해야 한다
  • ⑨ ICU 없는 타임존·달력·collation: 타임존 인지 타임스탬프·달력·collation이 항상 ICU 라이브러리로 구동되었다. ICU는 좋은 라이브러리지만 우리는 아주 작은 조각만 쓰면서 모든 배포판에 그것을 들고 다녔다. v2.0에서 ICU가 완전히 사라지고 icu 확장이 직접 구현하며 타임존 데이터는 IANA DB에서 직접 빌드되어 약 45kB로 압축된다. 훨씬 작고 최신 유지가 쉬울 뿐 아니라 단순히 더 빠르다
  • ⑩ 확장을 한 번 쓰고 직접 호스팅하기: 오늘날 대부분의 확장이 unstable C++ API에 대해 빌드되어, 확장 자체가 바뀌지 않아도 작성자가 모든 DuckDB 릴리스마다 재빌드·재발행해야 한다. 재빌드하지 않으면 최신 버전에 설치할 수 없다
    • v2.0은 개편된 C API를 제공한다. YAML로 표현된 버전화된 명세(역시 버전화된 명세 스키마 사용)와 코드 생성 도구를 갖추고, 스키마가 모든 심볼에 생명주기와 안정성 보장을 태깅할 수 있게 한다. API의 큰 부분이 stable·frozen으로 표시되어 DuckDB 버전 전반에 안정적 ABI를 제공한다
    • C API를 직접 프로그래밍할 필요는 없다 — 그 위에 C++ API도 제공하는데, 확장에 컴파일되어 들어가고 stable C ABI와만 대화하는 얇은 레이어라 배포하는 바이너리가 DuckDB 버전과 독립적으로 유지된다. Rust 바인딩도 작업 중
    • 배포: 지금까지 DuckDB는 내장 리포지토리에서만 확장을 설치할 수 있었지만, v2.0에서 자체 신뢰 리포지토리를 등록할 수 있어 조직이 자기 확장을 호스팅·서명하고 내장 것처럼 설치·로드하게 할 수 있다. 리포지토리는 이름, URL prefix, 그리고 거기서 서빙되는 확장에 서명할 것으로 신뢰되는 하나 이상의 RSA 공개 키다. CREATE 시점에 DuckDB가 공개 키를 가져와 정의에 pin하고 각 키의 SHA-256 fingerprint를 출력해 별도로 발행된 것과 비교할 수 있게 한다. 네트워크를 전혀 신뢰하고 싶지 않으면 키를 직접 전달할 수도 있다. pin된 리포지토리는 재시작을 견디고, 여러 키를 신뢰해 key rotation을 지원하고, 언제든 감사 가능

주요 수치 / 사실

  • v2.0 코드명 “Cyanoptera” (cinnamon teal). v1.5 이후 10,000개 이상의 커밋
  • recursive CTE 마이크로벤치마크: 100만 엣지 그래프의 single-source reachability — v1.5.4가 4.90초, v2.0 프리뷰가 0.12초. 약 40배 빠름
  • ICU 제거 후 마이크로벤치마크: 2,500만 row의 AT TIME ZONE 'Europe/Paris'가 0.24초 → 0.11초(2.2배), 500만 row의 COLLATE de 필터가 0.15초 → 0.06초(2.6배)
  • 주요 breaking change: 새 기본 스토리지 포맷과 lambda 문법 전환 완료
  • DuckDB Foundation에 stakeholder advisory board 추가 예정 — DuckDB·DuckLake·Quack의 개발 로드맵에 핵심 이해관계자가 목소리를 낼 수 있게

관련 위키


Source: 원문 보기