모범 사례
모범 사례 (Best practices)
데이터 레이크 쿼리를 위한 접근 방법 선택, 성능 튜닝, 운영 디버깅 요령을 정리한 가이드예요.
출처: 문서
본문
시작하기 가이드는 Apache Iceberg, Delta Lake, Apache Hudi, Apache Paimon을 처음으로 조회하는 과정을 안내해요. 설정을 마친 뒤에는 이 페이지를 사용해 올바른 접근 패턴을 선택하고, 쿼리 성능을 튜닝하고, 프로덕션에서 레이크 쿼리를 디버깅할 수 있어요.
접근 방법 선택하기
| 접근 방법 | 언제 사용할까 | 예시 |
|---|---|---|
| 테이블 함수 | 알려진 경로에 대한 임시(ad hoc) 쿼리 | icebergS3(), deltaLake(), hudi(), paimon() |
| 테이블 엔진 | 카탈로그 없이 같은 경로에 대한 반복 쿼리 | IcebergS3, DeltaLake, Hudi |
DataLakeCatalog 데이터베이스 엔진 |
카탈로그가 있는 프로덕션 워크로드. 여러 테이블에 걸친 페더레이션 쿼리 | AWS Glue, Unity Catalog, REST catalog |
테이블 함수
위치를 알고 영구 테이블 정의가 필요 없을 때 스토리지 경로와 자격 증명을 인라인으로 전달해요.
SELECT count()
FROM icebergS3('https://my-bucket.s3.amazonaws.com/warehouse/my_table/')
WHERE event_date >= today() - 7
AWS S3와 GCS에는 S3 변형을 사용해요. Azure와 로컬 파일시스템에는 전용 변형(icebergAzure, icebergLocal 및 다른 포맷의 등가물)이 있어요. 전체 목록은 직접 조회하기를 참고하세요. Paimon은 테이블 함수와 실험적 테이블 엔진을 제공해요.
테이블 엔진
같은 경로를 반복해서 조회할 때 테이블 엔진으로 테이블을 만들어요. ClickHouse가 경로와 자격 증명을 테이블 메타데이터에 저장하므로, 매번 함수 호출을 재구성하는 대신 일반 테이블 이름을 조회하면 돼요.
CREATE TABLE events
ENGINE = IcebergS3('https://my-bucket.s3.amazonaws.com/warehouse/events/')
SELECT count() FROM events WHERE event_date = today()
테이블 엔진은 데이터 캐싱과 메타데이터 캐싱을 포함해 테이블 함수와 동일한 읽기 기능을 지원해요. 데이터는 ClickHouse에 절대 중복되지 않아요. 팀과 접근을 공유하거나 같은 테이블에 예약 작업을 실행할 때 테이블 엔진이 유용해요.
DataLakeCatalog 데이터베이스 엔진
테이블이 외부 데이터 카탈로그에 등록되어 있을 때 ClickHouse를 한 번 연결해요. 연결을 만든 후 상류에서 추가된 테이블을 포함해 모든 카탈로그 테이블이 자동으로 ClickHouse 테이블로 나타나요.
CREATE DATABASE my_lake
ENGINE = DataLakeCatalog
SETTINGS
catalog_type = 'glue',
region = 'us-east-1',
aws_access_key_id = '<key>',
aws_secret_access_key = '<secret>'
SELECT count() FROM my_lake.`analytics.events`
이 방식은 많은 테이블이나 여러 카탈로그를 관리할 때 개별 테이블 정의를 만드는 것보다 확장성이 뛰어나요. 카탈로그 연결하기와 카탈로그 가이드를 참고하세요.
다중 부분 테이블 이름에는 백틱 카탈로그는 흔히 database.table 이름을 사용해요. 위 예시처럼 데이터베이스 한정 이름을 백틱으로 감싸 주세요.
필수 설정
많은 통합 기능은 처음 사용하기 전에 기능 플래그(feature flag)가 필요해요. CREATE DATABASE가 권한 오류로 실패하면 서비스 버전을 확인해 보세요. 카탈로그 연결의 경우 각 카탈로그 유형마다 고유한 플래그가 있어요. 개요는 카탈로그 연결하기를, 설정 세부 사항은 DataLakeCatalog 참조를 참고하세요. 카탈로그별 설정은 카탈로그 가이드에 있어요. 쓰기의 경우 Iceberg는 allow_insert_into_iceberg(25.7+, 26.2부터 Beta)가 필요해요. 데이터 레이크에 쓰기를 참고하세요. Delta Lake는 allow_delta_lake_writes(25.9+)가 필요해요. 지원 매트릭스는 각 포맷과 작업에 적용되는 플래그를 나열해요.
쿼리 성능 개선하기
이 페이지의 버전 번호는 ClickHouse 릴리스 버전(Cloud 및 자체 관리)과 일치해요. 설정이나 기능을 활성화하기 전에 서비스 버전을 확인해 주세요. 레이크 쿼리 성능은 ClickHouse가 오브젝트 스토리지에서 읽는 메타데이터와 Parquet 파일의 양에 달려 있어요. 다른 ClickHouse 테이블과 마찬가지로 파티션 컬럼으로 필터링하고 더 적은 컬럼을 선택하면 쿼리 성능이 개선돼요.
쿼리 습관
WHERE에서 파티션 컬럼으로 필터링해요. Iceberg와 Delta Lake는 ClickHouse가 쿼리 계획 중 관련 없는 파일을 건너뛸 수 있게 해주는 파티션 메타데이터를 저장해요. 필터가 파티션 스펙 밖의 컬럼을 대상으로 하면 ClickHouse는 일치하는 모든 파일을 스캔해요. 숨겨진 파티셔닝(hidden partitioning)이 있는 Iceberg 테이블에서는 별도의 파티션 컬럼이나 변형된 필드 이름이 아니라 테이블 스키마의 원본 컬럼으로 필터링해요. 테이블이 day(event_time)으로 파티셔닝되어 있다면 event_time에 술어를 추가해요. ClickHouse는 Iceberg 파티션 스펙을 사용해 그 필터에서 파티션 프루닝을 도출해요. 파티션 프루닝과 Iceberg 스펙을 참고하세요.
SELECT count()
FROM my_lake.`logs.application`
WHERE event_time >= '2026-03-01'
AND event_time < '2026-03-02'
SELECT * 대신 필요한 컬럼만 나열해요. ClickHouse는 오브젝트 스토리지에서 Parquet을 컬럼별로 읽으므로, 더 좁은 선택은 전송되고 압축 해제되는 바이트를 줄여요. 선택적인 필터는 WHERE에 둬요. ClickHouse 26.2+부터는 PREWHERE도 Iceberg와 다른 레이크 테이블 읽기에서 지원되어, 나머지 컬럼을 읽기 전에 Parquet 레이어에서 필터링해요. 파티션 프루닝은 여전히 파티션 원본 컬럼 필터링에 의존하며 PREWHERE만으로는 안 돼요. position 또는 equality delete가 많은 Iceberg 테이블은 스캔 중 merge-on-read 필터링을 적용해요. 매니페스트 프루닝만으로 예상하는 것보다 파일당 작업이 더 많을 수 있어요. 다중 노드 배포에서는 클러스터 테이블 함수를 사용해 파일 읽기를 레플리카에 분산해요.
다중 노드 클러스터에서 병렬 읽기
ClickHouse Cloud와 자체 관리 다중 노드 서비스에서는 레이크 테이블 함수의 클러스터 변형이 Parquet 파일 읽기를 레플리카에 분산해요. 이니시에이터 노드가 파일을 워커에 병렬로 디스패치해요. 대용량 테이블에 대한 배치 읽기와 예약 로드에는 클러스터 변형을 사용해요. 단일 노드 배포에서는 표준 테이블 함수로 충분해요. 첫 번째 인자로 클러스터 이름을 전달해요(ClickHouse Cloud에서는 'default'). 클러스터 변형은 모든 지원 포맷에 존재해요:
| 포맷 | 클러스터 함수 |
|---|---|
| Iceberg | icebergS3Cluster(), icebergAzureCluster() |
| Delta Lake | deltaLakeCluster(), deltaLakeAzureCluster() |
| Hudi | hudiCluster() |
| Paimon | paimonS3Cluster() |
클러스터 읽기를 다른 성능 설정과 결합할 수 있어요.
클러스터 함수와 On-Demand Compute
icebergS3Cluster와 deltaLakeCluster 같은 클러스터 변형은 파일 읽기를 기존 클러스터의 노드에 분산해요. 클러스터 함수 자체는 컴퓨팅을 추가하지 않아요. On-Demand Compute는 관리되는 풀에서 적격 쿼리에 임시로 추가 워커를 할당하는 ClickHouse Cloud 기능이에요. 기존 서비스와 엔드포인트를 통해 동작해요. 비공개 프리뷰 기간 동안 On-Demand Compute는 지원되는 Apache Iceberg와 Delta Lake 데이터에 대한 적격 SELECT 쿼리만 지원해요. 자격과 제한 사항은 On-Demand Compute 문서를 참고하세요.
배치 읽기를 스냅샷에 바운드하기
레이크 테이블에서 반복적으로 배치 로드를 하려면 각 실행을 전체 테이블을 다시 읽는 대신 스냅샷 범위로 한정해요. 바운드가 없으면 ClickHouse는 실행마다 모든 버전과 파일을 스캔할 수 있어, 오브젝트 스토리지 읽기와 쿼리 시간이 늘어나요. 마지막 성공한 로드의 스냅샷 식별자를 저장하고 다음 실행의 하한으로 사용해요.
- Iceberg의 경우 iceberg_snapshot_id 또는 iceberg_timestamp_ms(25.4+)로 특정 시점 뷰를 읽어요. append-only 테이블의 경우 스냅샷 설정을
WHERE의 파티션 필터와 결합해요. system.iceberg_history(25.6+)를 사용해 실행 사이의 스냅샷 ID를 조회해요. - Delta Lake의 경우 delta_lake_snapshot_start_version과 delta_lake_snapshot_end_version(25.12+)으로 두 버전 사이의 변경 사항을 읽어요. delta_lake_snapshot_version(25.8+)으로 단일 스냅샷을 읽어요. CDF 예시는 Delta change data feed를 참고하세요.
Parquet 파일 로컬 캐싱
두 포맷 모두 enable_filesystem_cache를 준수해 쿼리 사이에 인기 있는 Parquet 파일을 로컬 디스크에 유지해요. 자체 관리 배포에서는 설정이 쓸 저장 공간을 가지도록 서버 구성에서 파일시스템 캐시 디스크를 구성해요. ClickHouse Cloud는 캐싱을 자동으로 관리해요. 벤치마킹할 때는 캐시 히트가 실행 간 변경을 가리지 않도록 enable_filesystem_cache = 0으로 설정해요.
Apache Iceberg
대부분의 Iceberg 읽기 최적화는 기본적으로 켜져 있어요. 아래 설정은 파티션 프루닝, 메타데이터 캐싱, 카탈로그 왕복(roundtrip)을 제어해요.
읽기 설정
| 설정 | 도입 | 기본값 | 참고 |
|---|---|---|---|
| use_iceberg_partition_pruning | 25.1 | 25.6부터 1 |
매니페스트의 파티션 메타데이터로 데이터 파일 스킵 |
| use_iceberg_metadata_files_cache | 25.4 | 1 |
매니페스트 목록과 메타데이터 JSON을 메모리에 캐시 |
| iceberg_metadata_staleness_ms | 26.3 | 0 |
쿼리 설정. 매 쿼리마다 카탈로그를 호출하는 대신 이 창보다 최신일 때 캐시된 메타데이터 사용 |
| iceberg_use_version_hint | 25.6 | — | 직접 경로 접근 시 더 빠른 메타데이터 해석을 위해 version-hint.text 읽기 |
카탈로그 지연 줄이기
카탈로그에 연결된 Iceberg 테이블은 캐시하지 않으면 매 쿼리마다 메타데이터 가져오기 비용을 지불해요. 두 설정을 함께 사용해요(26.4+):
- 테이블 생성 시 iceberg_metadata_async_prefetch_period_ms를 설정해 백그라운드에서 메타데이터를 프리페치해요.
- 쿼리에서 iceberg_metadata_staleness_ms(26.3+)를 설정해 카탈로그 왕복을 건너뛰는 대가로 약간 오래된 메타데이터를 수용해요.
CREATE TABLE events
ENGINE = IcebergS3('https://my-bucket.s3.amazonaws.com/warehouse/events/')
SETTINGS iceberg_metadata_async_prefetch_period_ms = 60000;
SELECT count()
FROM events
SETTINGS iceberg_metadata_staleness_ms = 60000;
0의 staleness 값은 항상 최신 메타데이터를 가져와요. 테이블이 드물게 변경되는 읽기 중심 워크로드에서는 창을 늘려요. ClickHouse가 잘못된 메타데이터 파일을 선택하면(테이블 경로에 여러 .metadata.json 파일이 있는 경우) 테이블 생성 시 iceberg_metadata_file_path(25.4+) 또는 iceberg_metadata_table_uuid로 해석을 고정해요. 메타데이터 파일 해석을 참고하세요.
타임 트래블
iceberg_timestamp_ms 또는 iceberg_snapshot_id(둘 다 25.4+)로 과거 스냅샷을 읽어요. 같은 쿼리에 둘 다 설정하지 마세요. ID를 선택하기 전에 system.iceberg_history(25.6+)에서 스냅샷 계보를 확인해요. 반복 배치 로드에 대해서는 배치 읽기를 스냅샷에 바운드하기를 참고하세요.
SELECT count()
FROM my_iceberg_table
SETTINGS iceberg_timestamp_ms = 1714636800000
Iceberg 쓰기
allow_insert_into_iceberg(25.7+, 26.2부터 Beta) 외에도, 삽입 시 출력 파일 크기와 파티션 수를 제어할 수 있어요:
| 설정 | 도입 | 목적 |
|---|---|---|
| iceberg_insert_max_rows_in_data_file | 25.9 | 출력 데이터 파일당 행 한도 |
| iceberg_insert_max_bytes_in_data_file | 25.9 | 출력 데이터 파일당 바이트 한도 |
| iceberg_insert_max_partitions | 25.12 | 단일 삽입에서 쓰는 파티션 수 상한 |
데이터 레이크에 쓰기와 Iceberg 엔진 참조를 참고하세요.
Delta Lake
버전 25.6부터 ClickHouse는 Delta Lake Rust 커널(kernel)을 통해 S3와 GCS에서 Delta Lake를 읽어요. 설정 이름은 버전 26.8 이상에서 allow_delta_kernel_rs, 버전 25.5~26.7에서는 allow_experimental_delta_kernel_rs예요. Azure Blob Storage에서는 커널이 비활성화되어 있으므로 deltaLakeAzure()를 레거시 리더와 함께 사용해요. 커널이 없으면 파티션 프루닝, change data feed, 스냅샷 버전 읽기를 사용할 수 없어요.
Delta Kernel
파티션 프루닝, change data feed, 스냅샷 버전 읽기에는 Delta 커널 설정이 활성화되어야 해요. 25.5부터 S3와 GCS에서 기본 켜져 있어요. 명시적으로 활성화할 때는 여러분의 ClickHouse 버전에 맞는 이름을 사용해요. 버전 26.8 이상:
SET allow_delta_kernel_rs = 1;
버전 25.5~26.7:
SET allow_experimental_delta_kernel_rs = 1;
읽기 설정
| 설정 | 도입 | 기본값 | 참고 |
|---|---|---|---|
| delta_lake_enable_engine_predicate | 25.8 | 1 |
파티션 프루닝을 위해 필터를 커널로 푸시. Delta Kernel 필요 |
| delta_lake_reload_schema_for_consistency | 26.3 | 0 |
동시 작성자가 스키마를 진화시킬 때 매 쿼리 전에 스키마 재로드 |
| delta_lake_snapshot_start_version / delta_lake_snapshot_end_version | 25.12 | -1 |
두 스냅샷 버전 사이의 CDF 변경 사항 읽기. 상류에서 CDF 활성화 필요 |
| delta_lake_snapshot_version | 25.8 | -1 |
단일 과거 스냅샷 읽기. 최신이면 -1(0도 유효) |
deletion vector(26.2+)가 있는 테이블은 읽기 중 행 수준 필터링을 적용해요. ClickHouse가 자동으로 처리하지만, DV가 많은 테이블 스캔은 파일당 작업이 더 많아요.
Delta change data feed
두 Delta 스냅샷 사이에 변경된 행만 읽으려면 delta_lake_snapshot_start_version과 delta_lake_snapshot_end_version(25.12+)을 설정해요. 테이블은 상류에서 change data feed가 활성화되어 있어야 해요(delta.enableChangeDataFeed). 쿼리 설정에 시작과 끝 버전을 모두 설정해요. 끝 버전만 설정하면 오류가 발생해요.
SELECT *
FROM deltaLake('s3://my-bucket/warehouse/ga4_events/')
SETTINGS
delta_lake_snapshot_start_version = 42,
delta_lake_snapshot_end_version = 47
각 성공적인 로드 후 끝 버전을 저장하고 다음 실행의 시작 버전으로 전달해요. 결과에는 CDF 컬럼(_change_type, _commit_version, _commit_timestamp)이 포함돼요. 대상 테이블에 로드하기 전에 이것을 처리해요. 일반적인 스냅샷 패턴은 배치 읽기를 스냅샷에 바운드하기를 참고하세요.
Delta Lake 쓰기
allow_delta_lake_writes(25.9+) 외에도, 삽입 시 출력 파일 크기를 제어할 수 있어요:
| 설정 | 도입 | 목적 |
|---|---|---|
| delta_lake_insert_max_rows_in_data_file | 25.9 | 출력 데이터 파일당 행 한도 |
| delta_lake_insert_max_bytes_in_data_file | 25.9 | 출력 데이터 파일당 바이트 한도 |
SET allow_delta_lake_writes = 1;
INSERT INTO my_delta_table
SETTINGS
delta_lake_insert_max_rows_in_data_file = 1000000,
delta_lake_insert_max_bytes_in_data_file = 134217728
SELECT * FROM source_table
쓰기에는 S3 또는 GCS에서 Delta Kernel이 필요해요. 예시는 DeltaLake 엔진 참조를 참고하세요.
레이크 쿼리 디버깅하기
느리거나 예상치 못한 결과를 반환하는 레이크 쿼리는 대개 메타데이터 읽기, 파티션 프루닝, 또는 카탈로그 연결성 문제로 귀결돼요. 아래 확인 사항부터 시작하고, 필요하면 포맷별 메타데이터 로그를 사용해요.
카탈로그 연결성 검증하기
DataLakeCatalog로 CREATE DATABASE를 해도 자격 증명을 검증하지 않아요. 카탈로그 연결이 끊어져 있어도 데이터베이스는 존재할 수 있어요. ClickHouse 26.4부터는 가벼운 상태 확인을 실행할 수 있어요:
CHECK DATABASE my_lake;
이전 버전에서는 SHOW TABLES FROM my_lake로 연결성을 확인하고 오류 메시지를 검사해요. SHOW CREATE TABLE을 백틱으로 감싼 테이블 이름과 함께 사용해 해석된 스토리지 경로와 엔진 유형을 확인해요:
SHOW CREATE TABLE my_lake.`db.table`;
카탈로그 테이블이 system.tables에 나타나지 않으면 show_remote_databases_in_system_tables(25.8+)를 활성화해요. 카탈로그 테이블은 기본적으로 시스템 인트로스펙션에서 숨겨져 있어요. 26.6 이전 버전에서는 이전 이름인 show_data_lake_catalogs_in_system_tables를 사용해요.
어떤 파일이 읽히는지 확인하기
Iceberg와 Delta Lake는 모든 읽기에서 가상 컬럼(_path, _file, _size, _time, _etag)을 노출해요. _path로 그룹화해서 파티션 프루닝이 동작하는지 또는 쿼리가 예상보다 더 많은 파일을 스캔하는지 확인할 수 있어요. 숨겨진 파티셔닝이 있는 Iceberg 테이블의 경우 별도의 파티션 컬럼이 아니라 원본 컬럼(예: event_time)으로 필터링해요:
SELECT _path, count() AS rows
FROM my_lake.`logs.application`
WHERE event_time >= '2026-03-01'
AND event_time < '2026-03-02'
GROUP BY _path
ORDER BY rows DESC;
스캔 볼륨 확인하기
필터를 추가하거나 설정을 튜닝한 전후에 system.query_log의 read_rows와 read_bytes를 비교해요. ReadBufferFromS3Bytes, CachedReadBufferReadFromCacheBytes 같은 ProfileEvents는 오브젝트 스토리지에서 온 데이터가 로컬 캐시에서 온 데이터보다 얼마나 되는지 보여줘요. query_log와 EXPLAIN의 전체 워크스루는 느린 쿼리 진단하기를 참고하세요. 벤치마킹할 때는 캐시 히트가 실행 간 변경을 가리지 않도록 enable_filesystem_cache를 비활성화해요.
메타데이터 로그
ClickHouse는 메타데이터 수준 디버깅을 위해 세 개의 시스템 테이블을 노출해요. 쿼리 시점에만 로깅을 활성화해요. 지속적인 모니터링용이 아니에요.
| 시스템 테이블 | 포맷 | 도입 | 활성화 방법 | 용도 |
|---|---|---|---|---|
| system.iceberg_metadata_log | Iceberg | 25.9 | 쿼리의 iceberg_metadata_log_level | 읽은 메타데이터 파일과 파티션 프루닝 결정 추적 |
| system.iceberg_history | Iceberg | 25.6 | ClickHouse의 Iceberg 테이블에 대해 자동으로 채워짐 | 타임 트래블 쿼리 전에 스냅샷 계보 확인 |
| system.delta_lake_metadata_log | Delta Lake | 25.10 | 쿼리의 delta_lake_log_metadata = 1 |
Delta 메타데이터 파일과 스냅샷 해석 추적 |
로깅을 활성화한 상태로 쿼리를 실행하고, 로그를 플러시한 뒤 해당 query_id의 항목을 검사해요:
SELECT count() FROM my_iceberg_table
SETTINGS iceberg_metadata_log_level = 'manifest_file_entry';
SYSTEM FLUSH LOGS iceberg_metadata_log;
SELECT content_type, file_path, pruning_status
FROM system.iceberg_metadata_log
WHERE query_id = '<previous_query_id>';
ClickHouse Cloud에서는 로그 데이터가 각 노드에 로컬로 있어요. 레플리카 전체의 전체 그림을 보려면 clusterAllReplicas를 사용해요. 상세한 Iceberg 로그 레벨은 매니페스트 목록과 파일에 대한 메타데이터 캐싱을 비활성화해서 같은 테이블에 대한 이후 쿼리를 느리게 해요. 적극적으로 조사할 때만 높은 상세도를 사용해요. Delta Lake 술어 문제의 경우 delta_lake_throw_on_engine_predicate_error(25.8+)를 활성화해 커널이 필터를 푸시할 수 없을 때 빠르게 실패하게 해요. 컬럼 세부 사항과 상세도 옵션은 iceberg_metadata_log와 delta_lake_metadata_log 참조 페이지를 참고하세요.
다음 단계
- 시작하기 — 직접 조회부터 다시 쓰기까지의 종단간 워크스루
- 직접 조회하기 — 네 가지 포맷의 테이블 함수, 엔진, 클러스터 변형
- 카탈로그 연결하기 — Unity Catalog가 포함된
DataLakeCatalog설정 - 데이터 레이크에 쓰기 — Iceberg와 Delta Lake에 데이터 다시 쓰기
- 지원 매트릭스 — 포맷, 카탈로그, 스토리지 백엔드 간 기능 비교