데이터 쓰기

데이터 쓰기 (Writing data)

ClickHouse에서 데이터를 Iceberg 테이블로 다시 써서 장기 보관(offloading)이나 역방향 ETL(reverse ETL) 워크플로를 구성하는 방법을 살펴봐요.

출처: 문서

본문

이전 가이드에서 오픈 테이블 포맷을 원위치에서 조회하고 빠른 분석을 위해 MergeTree로 데이터를 로드했어요. 많은 아키텍처에서는 데이터가 반대 방향으로도 흘러야 해요 — ClickHouse에서 다시 오픈 테이블 포맷으로요. 이를 이끄는 두 가지 일반적인 시나리오가 있어요:

  • 장기 보관으로 오프로드 - 데이터가 실시간 분석 레이어인 ClickHouse에 도착해서 대시보드와 운영 보고를 구동해요. 데이터가 실시간 기간(windowing)을 지나 노후화되면, 상호운용 가능한 포맷으로 내구성 있고 비용 효율적인 보존을 위해 오브젝트 스토리지의 Iceberg로 써낼 수 있어요.
  • 역방향 ETL - ClickHouse 내부에서 수행된 변환, 집계, 강화(enrichment)가 다운스트림 도구와 다른 팀이 소비해야 하는 파생 데이터셋을 만들어내요. 이 결과를 Iceberg 테이블에 쓰면 더 넓은 데이터 생태계에서 사용할 수 있게 돼요.

두 경우 모두 INSERT INTO SELECT를 사용해 ClickHouse 테이블에서 오브젝트 스토리지에 저장된 Iceberg 테이블로 데이터를 이동할 수 있어요.

오픈 테이블 포맷에 쓰는 것은 현재 Iceberg 테이블에만 지원돼요. Delta Lake 테이블에 대한 부분 지원은 개발 중이에요. 테이블은 카탈로그로 관리되지 않아야 해요.

원본 데이터셋 준비하기

이 가이드에서는 UK Price Paid 데이터셋을 사용할게요. 잉글랜드와 웨일스의 모든 주거용 부동산 거래에 대한 공개 기록이에요.

MergeTree 테이블 만들고 채우기

CREATE DATABASE uk;
CREATE TABLE uk.uk_price_paid
(
    price UInt32,
    date Date,
    postcode1 LowCardinality(String),
    postcode2 LowCardinality(String),
    type Enum8('terraced' = 1, 'semi-detached' = 2, 'detached' = 3, 'flat' = 4, 'other' = 0),
    is_new UInt8,
    duration Enum8('freehold' = 1, 'leasehold' = 2, 'unknown' = 0),
    addr1 String,
    addr2 String,
    street LowCardinality(String),
    locality LowCardinality(String),
    town LowCardinality(String),
    district LowCardinality(String),
    county LowCardinality(String)
)
ENGINE = MergeTree
ORDER BY (postcode1, postcode2, addr1, addr2);

공개 CSV 소스에서 직접 테이블을 채울게요:

INSERT INTO uk.uk_price_paid
SELECT
    toUInt32(price_string) AS price,
    parseDateTimeBestEffortUS(time) AS date,
    splitByChar(' ', postcode)[1] AS postcode1,
    splitByChar(' ', postcode)[2] AS postcode2,
    transform(a, ['T', 'S', 'D', 'F', 'O'], ['terraced', 'semi-detached', 'detached', 'flat', 'other']) AS type,
    b = 'Y' AS is_new,
    transform(c, ['F', 'L', 'U'], ['freehold', 'leasehold', 'unknown']) AS duration,
    addr1,
    addr2,
    street,
    locality,
    town,
    district,
    county
FROM url(
    'http://prod1.publicdata.landregistry.gov.uk.s3-website-eu-west-1.amazonaws.com/pp-complete.csv',
    'CSV',
    'uuid_string String,
    price_string String,
    time String,
    postcode String,
    a String,
    b String,
    c String,
    addr1 String,
    addr2 String,
    street String,
    locality String,
    town String,
    district String,
    county String,
    d String,
    e String'
) SETTINGS max_http_get_redirects=10;
30906560 rows in set. Elapsed: 59.852 sec. Processed 30.91 million rows, 5.41 GB (516.39 thousand rows/s., 90.40 MB/s.)
Peak memory usage: 485.15 MiB.

Iceberg 테이블에 데이터 쓰기

Iceberg 테이블 만들기

기존 Iceberg 테이블에서 직접 읽으려면 icebergS3 테이블 함수를 권장해요. 이 가이드는 독립형 Iceberg 테이블을 만들고 채우는 것을 보여주므로, 명시적 스키마가 있는 IcebergS3 테이블 엔진을 사용해요. 예시 S3 위치에는 이미 완성된 테이블이 들어 있어요. CREATEINSERT 문을 실행하려면 두 위치를 여러분이 제어하는 비어 있고 쓰기 가능한 S3 프리픽스(prefix)와 자격 증명으로 바꿔야 해요. 스키마는 MergeTree 원본에 비해 단순화되어야 한다는 점에 유의하세요. ClickHouse는 Iceberg와 기본 Parquet 파일보다 더 풍부한 타입 시스템을 지원해요. Enum, LowCardinality, UInt8 같은 타입은 Iceberg에서 지원되지 않으므로 호환 가능한 타입으로 매핑해야 해요.

CREATE TABLE uk.uk_iceberg
(
    price UInt32,
    date Date,
    postcode1 String,
    postcode2 String,
    type UInt32,
    is_new UInt32,
    duration UInt32,
    addr1 String,
    addr2 String,
    street String,
    locality String,
    town String,
    district String,
    county String
)
ENGINE = IcebergS3('https://datasets-documentation.s3.amazonaws.com/lake_formats/iceberg_uk_price_paid/', '<aws_access_key>', '<aws_secret_key>', '<session_token>')

데이터 부분집합 삽입하기

INSERT INTO SELECT를 사용해 MergeTree 테이블에서 Iceberg 테이블로 데이터를 쓸게요. 이 예시에서는 London 거래만 쓰게 돼요. 이 예시는 allow_insert_into_iceberg 설정이 필요해요. 현재 성숙도와 제한 사항은 지원 매트릭스를 참고해 주세요.

SET allow_insert_into_iceberg = 1;
INSERT INTO uk.uk_iceberg SELECT *
FROM uk.uk_price_paid
WHERE town = 'LONDON'
2346741 rows in set. Elapsed: 1.419 sec. Processed 30.91 million rows, 153.43 MB (21.78 million rows/s., 108.15 MB/s.)
Peak memory usage: 371.60 MiB.

Iceberg 테이블 조회하기

이제 데이터가 오브젝트 스토리지에 Iceberg로 저장되었고, ClickHouse — 또는 Iceberg를 읽는 다른 도구 — 에서 조회할 수 있어요:

SELECT
    locality,
    count()
FROM uk.uk_iceberg
WHERE locality != ''
GROUP BY locality
ORDER BY count() DESC
LIMIT 10
┌─locality────┬─count()─┐
│ LONDON      │  896796 │
│ WALTHAMSTOW │    8610 │
│ LEYTON      │    3525 │
│ CHINGFORD   │    3133 │
│ HORNSEY     │    2794 │
│ STREATHAM   │    2760 │
│ WOOD GREEN  │    2443 │
│ ACTON       │    2155 │
│ LEYTONSTONE │    2102 │
│ EAST HAM    │    2085 │
└─────────────┴─────────┘

10 rows in set. Elapsed: 0.329 sec. Processed 457.86 thousand rows, 2.62 MB (1.39 million rows/s., 7.95 MB/s.)
Peak memory usage: 12.19 MiB.

집계 결과 쓰기

Iceberg 테이블은 원시 행을 저장하는 데만 국한되지 않아요. ClickHouse 내부에서 수행된 ETL 프로세스의 결과인 집계와 변환의 출력도 저장할 수 있어요. 이는 다운스트림 소비를 위해 미리 계산된 요약을 lakehouse에 게시하는 데 유용해요.

집계용 Iceberg 테이블 만들기

CREATE TABLE uk.uk_avg_town
(
    price Float64,
    town String
)
ENGINE = IcebergS3('https://datasets-documentation.s3.amazonaws.com/lake_formats/iceberg_uk_avg_town/', '<aws_access_key>', '<aws_secret_key>', '<session_token>')

집계 데이터 삽입하기

도시별 평균 부동산 가격을 계산하고 결과를 Iceberg에 직접 쓸게요:

INSERT INTO uk.uk_avg_town SELECT
    avg(price) AS price,
    town
FROM uk.uk_price_paid
GROUP BY town
1173 rows in set. Elapsed: 0.480 sec. Processed 30.91 million rows, 185.44 MB (64.34 million rows/s., 386.05 MB/s.)
Peak memory usage: 4.18 MiB.

집계 테이블 조회하기

이제 다른 도구 — 그리고 다른 ClickHouse 인스턴스 — 가 이 미리 계산된 데이터셋을 읽을 수 있어요:

SELECT
    town,
    price
FROM uk.uk_avg_town
ORDER BY price DESC
LIMIT 10
┌─town───────────────┬──────────────price─┐
│ GATWICK            │ 28232811.583333332 │
│ THORNHILL          │             985000 │
│ VIRGINIA WATER     │  984633.2938574939 │
│ CHALFONT ST GILES  │  863347.7280187573 │
│ COBHAM             │    775251.47313278 │
│ PURFLEET-ON-THAMES │           772651.8 │
│ BEACONSFIELD       │  746052.9327405858 │
│ ESHER              │  686708.4969745865 │
│ KESTON             │  654541.1774842045 │
│ GERRARDS CROSS     │  639109.4084023251 │
└────────────────────┴────────────────────┘

10 rows in set. Elapsed: 0.210 sec.

더 알아보기 (Learn more)