본문으로 건너뛰기
최종 수정: 에

VARIANT 데이터 타입

요약 Apache Doris VARIANT는 시간이 지나면서 구조가 변하는 JSON을 위한 컬럼 타입입니다. Apache Doris는 쓰기 시 각 문서를 파싱하고 JSON 경로별로 타입을 추론한 뒤, 자주 사용하는 경로를 독립된 컬럼형 서브컬럼으로 저장합니다. 기본 한도는 VARIANT 컬럼당 서브컬럼 2048개입니다. 이렇게 하면 v['user_id']에 대한 필터와 집계는 전체 문서를 파싱하는 대신 컬럼 하나만 읽습니다. Snowflake와 ClickHouse에서 영감을 받은 VARIANT는 Apache Doris 2.1.0(2024년 삼월)에서 정식 출시되었습니다.

Apache Doris VARIANT 데이터 타입: 고정된 스키마 없이 JSON 데이터를 적재하고, 자주 사용하는 경로를 네이티브 컬럼형 서브컬럼으로 저장해 컬럼 스토어 수준의 분석 속도를 제공하는 컬럼 타입입니다.

Apache Doris에서 VARIANT 데이터 타입을 사용하는 이유는?​

Apache Doris VARIANT 데이터 타입을 사용하면 유연한 JSON 저장과 컬럼 스토어의 쿼리 속도 사이에서 타협할 필요가 없습니다. OLAP 시스템에 JSON을 저장할 때는 오랫동안 이런 타협이 필요했습니다.

  • STRING이나 JSONB 컬럼은 유연성을 제공하지만, 특정 경로에 접근하는 쿼리는 매번 전체 문서를 파싱합니다. 십억 개 행에 대해 WHERE payload['action'] = 'opened'를 실행하면, action이 큰 문서 안의 짧은 필드 하나에 불과하더라도 모든 페이로드의 모든 바이트를 읽습니다.
  • 정적 컬럼은 빠르지만 스키마가 안정적일 때만 그렇습니다. 데이터 생산자가 다음 스프린트에 필드를 추가하면 다시 ALTER TABLE 마이그레이션을 수행하거나 별도의 JSON 컬럼을 두어야 합니다.

Apache Doris VARIANT를 사용하면 둘 중 하나를 선택할 필요가 없습니다. 컬럼 하나를 선언하고 JSON을 쓰면, Apache Doris가 자주 사용하는 경로를 실제 컬럼형 스토리지로 자동 변환합니다. 쿼리는 계속 SQL로 작성하며, 디스크에 저장된 테이블은 직접 설계한 테이블과 상당히 비슷한 구조를 갖습니다.

Apache Doris VARIANT 데이터 타입이란?​

Apache Doris VARIANT 데이터 타입은 DataTypeVariant와 ColumnVariant를 기반으로 하는 반정형 컬럼 타입입니다. 이전 이름은 ColumnObject이며, ClickHouse의 설계를 바탕으로 합니다. VARIANT는 스칼라, 일차원 배열, 중첩 객체 등 유효한 모든 JSON 값을 받습니다. 행이 들어오면 쓰기 처리기가 경로의 접두사 트리를 구성하고, 경로별로 타입을 추론한 뒤, 각 경로를 세그먼트 내부의 독립된 컬럼으로 기록합니다. 각 컬럼에는 페이지 단위 인코딩과 인덱스가 적용됩니다. 쿼리에서는 JSON에 접근하지만, 디스크에는 경로마다 서브컬럼이 저장됩니다.

주요 용어

  • Subcolumn: JSON 경로에서 생성되는 독립된 디스크 컬럼입니다. 정적 컬럼과 마찬가지로 각 서브컬럼에는 자체 인코딩, 압축, 존 맵, 블룸 필터가 있습니다.
  • Subcolumnization: 쓰기 시 JSON에서 자주 사용하는 경로를 서브컬럼으로 승격하는 과정입니다. variant_max_subcolumns_count로 제어하며, 기본값은 2048입니다.
  • Schema Template: 선택한 경로를 고정된 타입으로 지정하는 선택적 VARIANT<'path' : TYPE, ...> 절입니다. 이를 통해 데이터를 반복 적재해도 스토리지와 인덱스 구조를 안정적으로 유지할 수 있습니다.
  • Sparse column: 경로 수가 서브컬럼 한도를 초과하면 사용 빈도가 낮은 경로를 저장하는 공용 대체 컬럼입니다. 물리적 컬럼 하나에 여러 논리적 경로를 저장합니다.
  • Type promotion: 같은 경로가 어떤 행에서는 INT로, 다른 행에서는 STRING으로 들어올 때 처리 방법을 결정하는 규칙입니다. 호환되는 타입은 더 넓은 타입으로 확장되며(TINYINT에서 BIGINT로), 호환되지 않는 타입은 JSONB로 대체됩니다.

Apache Doris VARIANT 데이터 타입은 어떻게 동작하나요?​

Apache Doris VARIANT는 쓰기 시 각 JSON 문서를 파싱하고, JSON 경로별로 타입을 추론하며, 자주 등장하는 경로를 독립된 컬럼형 서브컬럼으로 저장합니다. 사용 빈도가 낮은 경로는 공용 희소 컬럼에 저장합니다. 경로 하나의 디스크 저장 수명 주기는 다음과 같습니다.

  1. 파싱 및 추론. JSON이 memtable에 들어오면 쓰기 처리기는 모든 리프 경로를 접두사 트리에 추가하고, 값을 담을 수 있는 가장 작은 타입을 선택합니다. 타입은 BIGINT, DOUBLE, STRING, 일차원 ARRAY<T>, 중첩된 VARIANT 중에서 선택합니다.
  2. 승격 또는 대체. 새 값이 현재 타입과 충돌하면 Doris는 가능한 경우 타입을 확장합니다(TINYINT와 DOUBLE이 만나면 DOUBLE이 됩니다). 확장할 수 없으면 해당 경로는 JSONB로 대체되며, 스키마를 바로잡을 때까지 해당 경로에 대한 쿼리는 푸시다운을 사용할 수 없습니다.
  3. 서브컬럼 또는 희소 컬럼에 기록. NULL이 아닌 값의 비율이 가장 높은 경로부터 독립된 서브컬럼으로 저장하며, 정적 컬럼과 똑같이 인코딩하고 압축합니다. variant_max_subcolumns_count에 도달하면 나머지 저빈도 경로는 희소 컬럼에 묶어서 저장합니다.
  4. 스키마 병합. 각 rowset은 자체 서브컬럼 스키마를 기록합니다. 컴팩션은 최소 공통 컬럼 스키마를 사용해 rowset을 병합하므로, 오늘 새 필드를 추가하더라도 ALTER TABLE을 실행할 필요가 없습니다.
  5. 경로별 읽기. SELECT v['user_id']는 서브컬럼 하나로 해석되어 해당 컬럼만 읽으며, 정적 컬럼과 마찬가지로 존 맵과 블룸 필터를 사용해 페이지를 건너뜁니다. 필터 조건도 푸시다운됩니다. CAST(v['user_id'] AS BIGINT) = 42는 JSON 텍스트가 아니라 타입이 지정된 서브컬럼을 대상으로 실행됩니다.

역색인도 하위 경로에 같은 방식으로 동작합니다. INDEX idx_v(v) USING INVERTED PROPERTIES("parser" = "english")를 사용하면 모든 텍스트 서브컬럼을 검색할 수 있으며, Schema Template을 사용하면 특정 경로 하나에 인덱스를 지정할 수 있습니다.

빠른 시작​

CREATE TABLE github_events (
id BIGINT,
type VARCHAR(30),
payload VARIANT,
created_at DATETIME,
INDEX idx_payload (payload) USING INVERTED PROPERTIES("parser" = "english")
)
DUPLICATE KEY(id)
DISTRIBUTED BY HASH(id) BUCKETS 4
PROPERTIES ("storage_format" = "V3");

INSERT INTO github_events VALUES
(1, 'PushEvent', '{"action":"opened","commits":[{"sha":"abc"}],"size":4}', '2026-05-01'),
(2, 'IssueCommentEvent', '{"action":"created","issue":{"number":42}}', '2026-05-01');

SELECT CAST(payload['action'] AS STRING) AS action, COUNT(*) AS n
FROM github_events
WHERE payload['action'] MATCH 'opened'
GROUP BY action;

예상 결과

+--------+------+
| action | n |
+--------+------+
| opened | 1 |
+--------+------+

필터는 서브컬럼 하나만 읽고 역색인을 사용하며, commits 배열에는 전혀 접근하지 않습니다. SET describe_extend_variant_column = true; DESC github_events;를 실행하면 추론된 서브컬럼(payload.action, payload.commits, payload.issue.number, ...)을 직접 선언한 컬럼처럼 확인할 수 있습니다.

Apache Doris VARIANT 데이터 타입은 언제 사용해야 하나요?​

JSON 스키마는 계속 변하지만 대부분의 쿼리가 익숙한 소수의 경로에 접근한다면 Apache Doris VARIANT를 사용하세요. 구조가 고정되어 있다면 정적 컬럼이나 STRUCT를 사용하고, 기본 키, 정렬 키, 조인 키에는 VARIANT를 사용하지 마세요.

적합한 경우

  • 대부분의 쿼리가 익숙한 몇몇 경로에 접근하고 문서의 나머지 부분은 함께 보관하기만 하는 이벤트 로그와 감사 페이로드.
  • 데이터 생산자가 계속 필드를 추가하며, 스키마 마이그레이션 때문에 데이터 적재를 중단할 수 없는 텔레메트리 및 사용자 프로필 테이블.
  • 수천 개의 선택적 태그가 있는 관측 가능성 데이터. 자주 사용하는 태그는 서브컬럼이 되고, 사용 빈도가 낮은 태그는 희소 컬럼에 저장되므로 컴팩션 부담이 커지지 않습니다.
  • VARIANT와 USING INVERTED를 조합한 JSON 내부의 전문 검색.

적합하지 않은 경우

  • 스키마가 안정적이고 명확하게 정의된 테이블. 일반적인 타입 지정 컬럼은 구조를 파악하고 인덱스를 생성하기가 더 쉬우며, 의도치 않게 JSONB로 승격될 일도 없습니다. 스키마가 실제로 변하는 경우에만 VARIANT를 선택하세요.
  • 기본 키, 정렬 키, 조인 키. VARIANT는 이러한 키로 사용할 수 없으며, 조인 조건으로 CAST(v['id'] AS BIGINT)를 사용하면 플래너가 선택할 수 있는 최적화 방법이 크게 줄어듭니다. id가 조인 키라면 정적 BIGINT로 선언하세요.
  • WHERE v LIKE '%doris%'와 같은 전체 문서 검색. 역색인은 컬럼 전체가 아니라 하위 경로에 존재합니다. 원본 JSON을 별도의 STRING 컬럼에도 저장하고 해당 컬럼에 인덱스를 생성하거나, DOC 모드를 활성화해 SELECT v를 빠르게 실행하세요.
  • 설계 시점에 이미 알고 있는 고정된 구조. 모든 행이 정확히 {a, b, c}로 구성되고 타입이 변하지 않는다면 STRUCT가 더 적합한 스키마 제약을 제공합니다. 예상치 못한 필드를 허용하지 않기 때문입니다. 반면 VARIANT는 이런 필드도 별도 경고 없이 받아들입니다.
  • 튜닝 없이 사용하는 매우 넓은 JSON(경로 10k+개). 기본 서브컬럼화 한도는 경로 2048개입니다. 이를 초과한다면 희소 컬럼이나 DOC 모드 사용을 계획하고, 프로덕션 데이터를 적재하기 전에 VARIANT 워크로드 가이드를 읽어보세요.

성능 / 수치​

  • ClickBench, 쿼리 43개를 16코어 / 64 GB EC2 인스턴스에서 실행: VARIANT는 12.7 GB를 사용해 JSON의 35.7 GB보다 약 65% 적은 공간을 사용했습니다. 워밍업 후 쿼리는 JSON보다 약 8배 빨랐으며, 미리 정의한 정적 컬럼과의 성능 차이는 약 10% 이내였습니다. 출처: Apache Doris 2.1.0의 Variant.
  • GuanceDB에서 관측 가능성 데이터를 Elasticsearch에서 VARIANT 기반 Doris로 마이그레이션: 서버 비용은 70% 줄었고, 전체 쿼리 속도는 약 2배, 단순 쿼리는 4배 이상 빨라졌습니다. 출처: 위와 같은 게시물.

더 읽어보기​