LLM WikiAccess-protected knowledge portal

WIKI

DuckDB 1.5 Variegata: VARIANT 타입과 PEG 파서로 분석 엔진의 경계를 넓히는 방법

로컬 분석 엔진이 왜 계속 경계를 넓히는가 DuckDB는 처음부터 "SQLite처럼 임베디드로 동작하는 OLAP 엔진"이라는 포지션을 유지해 왔다. 설치 없이 Python 패키지 하나로 쓸 수 있고, 단일 프로세스 안에서 Parquet, CSV, JSON, Iceberg 등 다양한 형식을 직접 쿼리한다. 이 설계는 데이터 엔지니어가 로컬에서 빠르게 탐색하고, 대형 분산 클러스터 없이도 GB 수준의 분석을 처리하는 용도에서 강점

경로human/study/content/database-frontier/07-duckdb-1-5-variegata-variant-type-peg-parser.md
카테고리Study
태그#mysql #parser #peg #study #type #variant #variegata

로컬 분석 엔진이 왜 계속 경계를 넓히는가

DuckDB는 처음부터 "SQLite처럼 임베디드로 동작하는 OLAP 엔진"이라는 포지션을 유지해 왔다. 설치 없이 Python 패키지 하나로 쓸 수 있고, 단일 프로세스 안에서 Parquet, CSV, JSON, Iceberg 등 다양한 형식을 직접 쿼리한다. 이 설계는 데이터 엔지니어가 로컬에서 빠르게 탐색하고, 대형 분산 클러스터 없이도 GB 수준의 분석을 처리하는 용도에서 강점을 발휘한다.

2026년 3월 9일 릴리스된 DuckDB 1.5.0("Variegata")은 이 경계를 더 넓혔다. VARIANT 타입 도입으로 반정형 데이터를 텍스트 파싱 없이 다룰 수 있게 됐고, GEOMETRY 타입으로 지리공간 데이터를 기본 타입으로 지원하기 시작했다. PEG 기반 파서와 CLI 재설계까지 합치면 사용자 경험 전반이 달라졌다. 2026년 6월 17일 릴리스된 1.5.4가 현재 안정 버전이다.

이 장은 2026년 7월 15일 기준으로 작성했다. DuckDB 1.5.0은 2026-03-09에 릴리스됐고 최신 패치는 1.5.4(2026-06-17)다. DuckDB 1.4.x LTS("Andium")는 2026년 9월까지 지원된다. DuckDB v2.0.0이 올 하반기에 예고되어 있다. VARIANT 타입의 일부 집계 쿼리는 특정 패턴에서 JSON 텍스트 파싱보다 느릴 수 있다는 알려진 이슈(#22024)가 있다.


VARIANT 타입: 반정형 데이터를 텍스트 없이 다루는 방법

JSON 타입의 한계

DuckDB에는 이미 JSON 타입이 있었다. 하지만 JSON 타입은 물리적으로 VARCHAR로 저장된다. 즉, 컬럼에 들어 있는 값은 결국 JSON 텍스트 문자열이다. json_extract(data, '$.user.name')을 호출할 때마다 DuckDB는 그 문자열을 파싱해 원하는 필드를 찾는다. 행이 1억 개라면 1억 번의 텍스트 파싱이 발생한다. 타입 정보가 없으니 필드가 숫자인지 문자열인지 저장된 값을 꺼내봐야 알 수 있다.

VARIANT 타입: 바이너리 인코딩, 행별 타입 정보

VARIANT는 이 구조를 근본적으로 바꾼다. 각 행의 VARIANT 값은 바이너리 인코딩으로 저장되며, 값 안에 타입 정보가 함께 들어 있다. 문자열, 숫자, 배열, 중첩 객체 등 각 필드의 타입이 이미 인코딩 안에 포함되어 있어 꺼낼 때 파싱이 필요 없다.

DuckDB: JSON 타입 vs VARIANT 타입 구조 비교 JSON 타입 (VARCHAR 저장) 저장 형태 '{"user":"alice","age":30}' — 텍스트 문자열 쿼리 경로 json_extract(data, '$.user') → 텍스트 파싱 → 필드 탐색 → 타입 추론 → 1억 행 = 1억 번 텍스트 파싱 Parquet 저장 시 BLOB으로 저장 (텍스트 그대로) 통계 없음 → Row Group 스킵 불가 프레디케이트 푸시다운 제한적 VARIANT 타입 (바이너리 인코딩) 저장 형태 [type:str|"alice"][type:int32|30] — 바이너리, 타입 포함 쿼리 경로 variant_extract(data, 'user') → 바이너리 오프셋 탐색 → 타입 확인 완료 → 파싱 없음, 직접 접근 (5~10x 빠름) Parquet 저장 시 (Shredding) 자주 쓰는 필드 → 별도 typed 컬럼으로 분리 통계(min/max) 생성 → Row Group 스킵 가능 프레디케이트 푸시다운 → Parquet 레이어에서 필터링 성능 비교 요약 일반적인 케이스 VARIANT: 5~10x 빠름 저장 공간: ~40% 절감 Shredded Parquet: 최대 100x 빠름 주의: 알려진 이슈 (#22024) 단순 집계(COUNT, SUM)에서 JSON 텍스트보다 최대 100x 느린 경우 도입 전 워크로드별 측정 필요 Snowflake 호환성 Snowflake VARIANT Parquet 파일 읽기 가능 (Shredded VARIANT 포함) Iceberg + VARIANT 조합으로 세미정형 Lakehouse
DuckDB VARIANT 타입 vs JSON 타입 — 저장 구조와 쿼리 경로 비교

VARIANT 사용 예시

-- JSON과 동일한 컬럼 생성 문법, 물리 저장은 다름
CREATE TABLE events (
  id BIGINT,
  data VARIANT
);

-- JSON 문자열을 VARIANT로 자동 변환
INSERT INTO events VALUES (1, '{"user": "alice", "score": 95}');
INSERT INTO events VALUES (2, '{"user": "bob",   "score": 87, "tags": ["a","b"]}');

-- variant_extract: 타입 인식 필드 접근
SELECT
  id,
  variant_extract(data, 'user')::VARCHAR  AS user_name,
  variant_extract(data, 'score')::INTEGER AS score
FROM events
WHERE variant_extract(data, 'score')::INTEGER > 90;

-- 닷 표기법도 지원 (1.5.x)
SELECT data.user FROM events;

json_extract와 달리 variant_extract는 파싱이 끝난 바이너리에서 직접 필드를 찾는다. 필드를 꺼낸 뒤 ::INTEGER 캐스트는 이미 인코딩된 타입 정보를 확인하는 과정이어서 텍스트 파싱보다 훨씬 빠르다.

VARIANT Shredding: Parquet에서의 성능 극대화

VARIANT의 성능이 가장 크게 발휘되는 곳은 Parquet 저장이다. DuckDB는 VARIANT 컬럼을 Parquet에 쓸 때 Shredding을 지원한다. Shredding은 VARIANT 안에서 자주 쓰이는 필드를 별도의 typed 컬럼으로 분리하는 기술이다.

예를 들어 data.score가 자주 조회된다면, Parquet 파일 안에 score 필드가 INT32 typed 컬럼으로 별도 저장된다. 이 컬럼에는 Parquet Row Group 통계(min/max)가 생성된다. 이후 WHERE score > 90 쿼리가 오면 DuckDB는 Row Group 통계를 확인해 대부분의 Row Group을 스킵할 수 있다. 실제로 읽는 행 수가 극적으로 줄어든다.

DuckDB 1.5는 Snowflake가 생성한 Shredded VARIANT Parquet 파일도 읽을 수 있다. Snowflake와 DuckDB 사이에서 VARIANT 데이터를 교환할 때 별도의 변환 없이 그대로 쿼리할 수 있다.


GEOMETRY 타입: 지리공간 데이터를 기본 타입으로

1.5 이전에는 지리공간 처리를 위해 spatial 익스텐션을 별도로 로드해야 했다. 1.5에서 GEOMETRY 타입이 기본 타입으로 승격됐다. 좌표 계(CRS, Coordinate Reference System) 정보, Parquet/Arrow 직렬화, 필터 푸시다운, 통계 처리가 통합됐다.

데이터 플랫폼에서 지리공간 데이터를 다루는 일반적인 시나리오는 두 가지다. 첫째, GPS 로그, 배달 경로, 지점 좌표 같은 점(Point) 데이터를 분석하는 경우. 둘째, 행정구역, 서비스 구역, 지도 폴리곤 데이터와 조인하는 경우. GEOMETRY 타입이 기본으로 통합됐으니 별도 로드 없이 이런 분석을 시작할 수 있다.

-- 두 지점 사이의 거리 계산 (기본 내장)
SELECT
  ST_Distance(
    ST_GeomFromText('POINT(127.0 37.5)'),  -- 서울
    ST_GeomFromText('POINT(129.0 35.1)')   -- 부산
  ) AS distance_deg;

GeoArrow CRS 직렬화 버그는 1.5.4에서 수정됐다. 지리공간 Parquet 파일을 다루고 있다면 1.5.0~1.5.3보다 1.5.4를 사용해야 한다.


PEG 파서: 더 나은 오류 메시지와 문법 확장성

1.5는 실험적 PEG(Parsing Expression Grammar) 파서를 도입했다. CREATE, SET, DELETE, ATTACH 구문이 PEG 트랜스포머 프레임워크 위에서 처리된다.

PEG 파서의 운영 가치는 두 가지다.

더 나은 오류 메시지: 기존 파서는 문법 오류 위치를 대략적으로만 알려줬다. PEG 파서는 실패한 위치를 토큰 단위로 정확하게 지시하고, 문맥에 맞는 오류 메시지를 제공한다. CREATE TABLE 구문에 오타가 있다면 어떤 토큰에서 어떤 것을 기대했는지 명확하게 나온다. 복잡한 DDL을 작성하거나 쿼리 파이프라인에서 자동 생성된 SQL을 디버깅할 때 오류 진단 시간이 줄어든다.

문법 확장성: 익스텐션이 PEG 트랜스포머를 통해 자체 SQL 문법을 추가할 수 있는 경로가 열렸다. 현재는 실험적이지만, 도메인 특화 SQL 방언을 DuckDB 위에서 구현하는 가능성을 연다.

달러 인용(dollar-quoted) 문자열과 세미콜론 처리의 엣지 케이스가 개선됐다. $$ 리터럴이 들어간 복잡한 쿼리가 제대로 파싱되지 않던 문제들이 해소됐다.


CLI 재설계: 인터랙티브 분석의 경험 개선

1.5의 CLI 재설계는 터미널에서 DuckDB를 대화형으로 사용하는 경험을 개선했다.

다크/라이트 모드 자동 감지: 터미널 테마에 맞춰 색상이 자동으로 조정된다. 밝은 배경 터미널에서는 결과가 가독성 있는 색상으로 출력된다.

중첩 타입 프리티 프린팅: LIST, STRUCT, MAP 같은 중첩 타입이 들어 있는 결과를 읽기 쉽게 들여쓰기해서 출력한다. VARIANT 컬럼을 직접 SELECT할 때도 계층 구조가 보기 좋게 표시된다.

페이저(Pager): 결과가 터미널 높이를 넘어가면 자동으로 페이저로 전환된다. q를 눌러 빠져나오거나 화살표 키로 스크롤할 수 있다.

마우스 클릭 지원: Ctrl+Q로 마우스 클릭 모드를 활성화하면 클릭으로 커서 위치를 이동할 수 있다.

자동완성 개선: 테이블명, 컬럼명, 함수명 자동완성이 데이터베이스/스키마 그룹핑과 함께 표시된다.

터미널에서 직접 데이터를 탐색하거나 빠른 쿼리 검증을 하는 작업 흐름에서 마찰이 줄어든다.


read_duckdb와 Azure Blob Storage 지원

read_duckdb 함수

1.5는 DuckDB 파일 자체를 테이블 함수로 읽는 read_duckdb를 도입했다. 다른 DuckDB 데이터베이스 파일의 테이블을 필터 푸시다운과 함께 조회할 수 있다.

-- 다른 DuckDB 파일의 테이블을 직접 쿼리
SELECT *
FROM read_duckdb('other.duckdb', 'sales')
WHERE region = 'KR' AND amount > 1000;

여러 DuckDB 파일에 데이터가 분산되어 있을 때 복사 없이 직접 JOIN하거나 필터링할 수 있다. 데이터를 여러 파일로 분리해 관리하는 파이프라인에서 유용하다.

Azure Blob Storage / ADLSv2 쓰기 지원

1.5 이전에는 Azure 환경에서 DuckDB의 쓰기 기능이 제한적이었다. 1.5에서 ADLSv2(Azure Data Lake Storage Gen2) 쓰기가 지원된다.

-- Parquet을 Azure Blob Storage에 직접 쓰기 (인증 설정 후)
COPY events TO 'abfs://[email protected]/events.parquet'
(FORMAT PARQUET);

AWS S3 쓰기는 이미 지원됐고, 1.5에서 Azure 쓰기가 추가됐다. GCP Cloud Storage도 지원된다. 클라우드 스토리지에 직접 쓰는 능력은 DuckDB를 로컬 분석 도구를 넘어 경량 ELT 도구로 사용할 수 있는 기반이 된다.


성능 개선 내역

1.5는 쿼리 실행 엔진에도 여러 최적화가 들어갔다.

블룸 필터 조인: 해시 조인에서 블룸 필터를 사용해 불일치 행을 미리 걸러낸다. 이전에는 Nested Loop으로 처리되던 일부 복잡한 JOIN이 블룸 필터 힌트를 통해 더 빠른 경로로 실행된다.

Late Materialization: TopN 윈도우 함수와 Parquet 연산에서 Late Materialization이 적용됐다. 필요한 컬럼만 최대한 늦게 읽어 I/O를 줄인다. LIMIT 10 ORDER BY score DESC 같은 TopN 패턴에서 효과가 크다.

공통 서브플랜 제거: 동일한 서브쿼리가 여러 번 참조될 때 중복 실행을 제거한다. WITH 절이나 뷰를 여러 번 참조하는 쿼리에서 실행 횟수가 줄어든다.

체크포인트 중 동시 읽기: 1.5 이전에는 체크포인트(WAL flush) 중 읽기 쿼리가 블록됐다. 1.5에서 체크포인트 중에도 읽기 쿼리가 동시에 실행될 수 있다. 지속적으로 쓰면서 동시에 읽는 파이프라인에서 읽기 레이턴시가 안정적으로 유지된다.


LTS 선택: 1.4.x Andium vs 1.5.x Variegata

1.5.x Variegata — 우선 채택 조건
반정형 데이터(JSON/이벤트 로그)를 VARIANT 타입으로 처리해 성능을 높이려는 경우.
Snowflake VARIANT Parquet 파일을 DuckDB에서 직접 쿼리해야 하는 경우.
Azure ADLSv2 쓰기가 필요한 클라우드 환경.
지리공간 데이터를 기본 타입으로 다루는 파이프라인.
신규 프로젝트라면 1.5.4부터 시작한다. GeoArrow CRS 버그, VARIANT 필터 버그가 1.5.4에서 수정됐다.
1.4.x Andium (LTS) — 유지 조건
프로덕션 파이프라인이 1.4.x에서 안정적으로 동작 중이고 신규 기능이 필요 없는 경우. 2026년 9월까지 보안 패치가 지속된다.
1.5의 DataPointer 직렬화 변경, 바인더 아키텍처 리팩터링이 기존 저장 파일이나 확장 코드에 영향을 줄 수 있는지 먼저 검토한다.
VARIANT 도입 전 워크로드 측정 필수
단순 집계(COUNT, SUM)를 VARIANT 컬럼에서 수행하는 쿼리는 JSON 텍스트보다 느릴 수 있다(이슈 #22024). 도입 전 실제 쿼리 패턴으로 before/after를 측정한다.
Shredded Parquet을 활용하는 집계 쿼리에서는 성능이 대폭 향상된다. 쓰기 시 shredding 설정을 명시해야 효과가 나타난다.
v2.0.0 대비
DuckDB v2.0.0이 2026년 하반기에 예고되어 있다. 신규 스토리지 형식 변경이 포함될 가능성이 있다. 중요 파이프라인 업그레이드 계획에서 v2.0.0 릴리스 노트를 확인하는 단계를 추가한다.
lockfile(requirements.txt, pyproject.toml의 duckdb 버전)을 고정해 의도치 않은 v2.0.0 자동 업그레이드를 방지한다.
DuckDB 버전 선택 기준

정리

DuckDB 1.5 Variegata는 단순한 버그픽스 릴리스가 아니다. VARIANT 타입은 반정형 데이터를 다루는 방식을 바꾼다. 텍스트 파싱 기반의 JSON 쿼리와 달리 바이너리 인코딩 + 행별 타입 정보로 접근하고, Parquet에서 Shredding을 통해 통계 기반 Row Group 스킵까지 가능하다. Snowflake VARIANT 파일 호환성은 실질적인 데이터 교환 경로를 만든다.

GEOMETRY 타입의 기본 통합은 지리공간 분석을 별도 익스텐션 로드 없이 시작할 수 있게 한다. PEG 파서는 오류 메시지 품질을 높이고 문법 확장 가능성을 열었다. CLI 재설계는 인터랙티브 탐색 경험을 개선했다.

도입 시 주의할 점은 VARIANT의 단순 집계 성능 이슈(#22024)다. 워크로드 패턴에 따라 JSON 텍스트보다 느릴 수 있으므로 측정 없이 일괄 전환을 결정하지 않는다. v2.0.0이 예고된 상황에서 버전 고정과 릴리스 노트 추적도 운영 체크리스트에 포함한다.

References