Skip to main content
이 가이드에서는 NYC Taxi 데이터셋에 두 가지 최적화 방식을 적용합니다. 먼저 더 적합한 컬럼 타입을 선택해 저장 및 처리되는 데이터 양을 줄입니다. 다음으로 선택적 쿼리에서 ClickHouse가 불필요한 데이터를 건너뛸 수 있도록 순서 지정 키를 도입합니다. 각 변경 사항은 동일한 기준선을 바탕으로 측정합니다. 이 예시에서 따르는 전반적인 워크플로는 쿼리 최적화 개요를 참조하십시오.

시작하기 전에

예시에서는 nyc_taxi.trips_small_inferred 테이블을 사용합니다. 아직 테이블을 생성하고 데이터를 로드하지 않았다면 다음을 수행하십시오:
원본 Parquet 파일의 크기는 약 5.8 GB입니다. 네트워크 환경과 사용 가능한 리소스에 따라 로드하는 데 몇 분 정도 걸릴 수 있습니다.
원본 Parquet 파일에는 약 3억 2,900만 개의 행이 포함되어 있습니다. 이 가이드의 소요 시간은 하나의 배포 환경에서 측정한 값이며, 사용 가능한 컴퓨트 리소스에 따라 달라질 수 있습니다. 동일한 소요 시간을 기대하기보다는 단계 간 상대적인 변화를 비교하십시오. 이 메서드를 자체 워크로드에 적용할 때는 쿼리나 스키마를 변경하기 전에 느린 쿼리 진단을 사용하여 반복되는 쿼리 패턴을 식별하고 대표적인 실행 사례를 선택하십시오.

프로세스 개요

이 예시는 다음 3단계로 진행됩니다.
  1. 기준선을 설정하기 위해 추론된 스키마를 대상으로 독립적인 워크로드 쿼리 3개를 실행합니다.
  2. 더 정확한 컬럼 타입을 사용해 테이블을 생성하고, 동일한 데이터를 로드한 후 쿼리를 다시 실행합니다.
  3. 동일하게 최적화된 스키마와 순서 지정 키를 사용해 다른 테이블을 생성한 후 쿼리를 다시 실행합니다.
스키마와 순서 지정 키를 별도 단계에서 변경하면 각각의 영향을 더 쉽게 구분할 수 있습니다. 최적화 접근 방식에서는 이러한 변경을 고려할 시점과 검증 방법을 설명합니다. 비교 가능한 측정값을 수집하는 방법은 쿼리 병목 현상 격리를 참조하십시오.

기준 워크로드 정의

워크로드를 실행한 클라이언트 세션에서 원격 데이터의 파일 시스템 캐시, 쿼리 캐시, 쿼리 조건 캐시를 비활성화합니다:
이러한 설정을 사용하면 테스트 중 반복 실행 결과를 비교할 수 있습니다. 측정이 끝나면 이전 값으로 복원하십시오.
다음 3개의 독립적인 쿼리가 기준 워크로드를 구성합니다. 다음 단계에서 생성하는 각 테이블에 대해 세 쿼리를 모두 실행하십시오. 비교 가능한 조건에서 각 쿼리를 여러 번 실행하고, 중앙값 등 대표적인 소요 시간과 읽은 행 수, 최대 메모리 사용량을 기록하십시오. system.query_log에서 이러한 값을 가져오는 방법을 포함한 전체 측정 워크플로는 반복 가능한 기준 설정를 참조하십시오.

계산된 이동 속도 필터링

이 쿼리는 이동 시간과 속도를 계산한 다음 시속 30마일을 초과하는 운행의 이동 거리 분포를 구합니다:

특정 날짜 범위의 운행 집계

이 쿼리는 2009년 1분기의 운행 건수, 거리, 평균 결제 금액을 계산합니다:

승객 수로 필터링

이 쿼리는 승객 수가 1명 또는 2명인 운행의 평균 운행 시간을 계산합니다:
원래 측정값은 다음과 같습니다. 세 쿼리 모두 테이블의 전체 행 수에 가까운 약 3억 2,900만 행을 읽습니다. 따라서 워크로드는 두 가지 측면에서 개선할 수 있습니다. 먼저 선택한 컬럼의 처리 비용을 줄이고, 필터가 허용하는 경우 선택되는 행 수를 줄입니다.

스키마 최적화

스키마 추론은 데이터셋 탐색을 시작하는 실용적인 방법이지만, 추론된 타입은 워크로드에 필요한 것보다 더 광범위하거나 허용 범위가 넓을 수 있습니다. 추론된 타입이 불필요하다고 단정하지 말고, 스키마를 변경하기 전에 데이터를 검토하십시오.

불필요한 널 허용 컬럼 피하기

Nullable 컬럼은 값 외에 널 마스크도 저장합니다. 널 값과 타입의 기본값을 구분해야 하는 경우에는 Nullable을 사용하되, 값이 항상 존재하는 컬럼에는 사용하지 마십시오. 예시 스키마에서 사용된 컬럼의 널 값 개수를 계산합니다:
이 데이터셋에서 null 값을 포함하는 컬럼은 ratecode_id, mta_tax, payment_type뿐입니다. 최적화된 스키마에서는 해당 컬럼에만 Nullable을 유지하고, 나머지 컬럼에서는 제거합니다.

반복 값에 LowCardinality 사용

LowCardinality는 딕셔너리 인코딩을 사용하며, 반복 값이 많은 컬럼의 저장 공간 사용량과 처리 비용을 줄일 수 있습니다. 적용하기 전에 고유 값의 수를 확인하십시오:
이 4개 컬럼은 행 수에 비해 고유 값의 수가 상당히 적습니다. LowCardinality 적용을 고려할 만하지만, 워크로드에 미치는 효과는 반드시 측정해야 합니다. 약 10,000개의 고유 값은 적용 후보를 찾는 데 유용한 출발점일 뿐, 고정된 기준은 아닙니다.

더 정밀한 데이터 타입 선택

필요한 범위와 정밀도를 안전하게 보존할 수 있는 가장 작은 타입을 사용하십시오. 예를 들어, 추론된 Int64 또는 Float64를 대체하기 전에 숫자 컬럼의 최솟값과 최댓값을 확인하십시오:
두 정수 컬럼은 모두 UInt8 범위에 들어가지만, passenger_count는 최대값인 255에 이릅니다. 이 예시에서는 trip_distanceFloat32를 사용하고 금액 값에는 Decimal32를 사용합니다. 이 데이터셋의 모든 값은 대상 범위에 들어가며, 워크로드가 집계 결과를 비교하므로 이 예시에서는 낮아진 부동 소수점 정밀도와 센트 단위의 금액 정밀도를 허용합니다. 정확한 원본 값이 필요한 경우에는 더 넓은 원본 타입을 유지하십시오. 예시 쿼리에는 초 미만 정밀도가 필요하지 않으므로, 예시에서는 동일한 UTC 시간대의 추론된 DateTime64 컬럼을 DateTime으로 대체합니다. 이러한 선택은 이 데이터셋에만 적용됩니다. 동일한 변경 사항을 적용하기 전에 프로덕션 데이터의 범위, 정밀도, NULL 허용 여부 요구 사항을 확인하십시오.

스키마 변경 사항 적용

이 단계에서 스키마 변경 사항을 독립적으로 측정할 수 있도록 순서 지정 키 없이 테이블을 생성합니다:
각 워크로드 쿼리에서 nyc_taxi.trips_small_inferrednyc_taxi.trips_small_no_pk로 바꾼 후 세 쿼리를 모두 다시 실행합니다. 원래 예시에서는 다음과 같은 대표 결과가 나왔습니다. 쿼리가 읽는 행 수는 동일하지만, 최적화된 스키마는 해당 행이 나타내는 데이터 양을 줄입니다. 따라서 선택되는 데이터를 변경하지 않고도 쿼리 실행 시간과 최대 메모리 사용량이 개선됩니다. 두 테이블의 디스크상 크기를 비교합니다.
이 데이터셋에서는 최적화된 스키마를 적용하면 압축 저장 공간이 약 34% 감소하여 7.38 GiB에서 4.89 GiB로 줄어듭니다.

순서 지정 키 최적화

MergeTree 제품군에서 순서 지정 키는 행이 디스크에 정렬되는 방식을 결정합니다. ClickHouse는 이 순서를 기반으로 희소 프라이머리 인덱스를 구축하여 쿼리 필터 조건을 충족할 수 없는 그래뉼을 건너뜁니다. 많은 트랜잭션 데이터베이스의 프라이머리 키와 달리 고유성을 보장하지는 않습니다. 순서 지정 키는 중요한 반복 쿼리에서 사용하는 필터를 반영해야 합니다. 컬럼 순서도 중요합니다. 쿼리가 유용한 접두사로 필터링할 때 키의 효과가 가장 큽니다. 카디널리티가 낮은 컬럼은 자주 필터링된다면 효과적인 선행 항목이 될 수 있으며, 시간 기반 워크로드에서는 시간 구성 요소도 유용한 경우가 많습니다. 자세한 선택 지침은 프라이머리 키 선택을 참조하십시오. 이 예시에서는 (passenger_count, pickup_datetime, dropoff_datetime)를 사용합니다. passenger_count는 고유값이 적고 승객 수 필터에 사용되며, pickup_datetime는 날짜 범위 집계에 사용됩니다. pickup_datetime가 첫 번째 컬럼은 아니지만, 선행 컬럼에 조건이 없더라도 ClickHouse는 뒤쪽 키 컬럼의 값을 사용해 데이터를 제외할 수 있습니다. 일반적으로 순서 지정 키의 유용한 접두사로 필터링하면 더 효과적으로 프루닝할 수 있습니다.

순서 지정 키 변경 적용

이전 단계에서 사용한 최적화된 스키마와 동일하게 테이블을 생성합니다. 순서 지정 키만 변경합니다:
각 워크로드 쿼리에서 테이블 이름을 nyc_taxi.trips_small_pk로 바꾼 후, 세 쿼리를 모두 다시 실행하십시오.

결과 비교

원본 가이드에서는 세 단계에 걸쳐 다음과 같은 측정값을 기록했습니다. 스키마 최적화는 저장 공간을 줄이고 선택한 값을 더 적은 비용으로 처리할 수 있게 합니다. 날짜 범위 집계에서는 ClickHouse가 날짜 범위 밖의 그래뉼을 건너뛸 수 있으므로 순서 지정 키가 가장 큰 추가 개선 효과를 제공합니다. 승객 수 필터도 첫 번째 키 컬럼을 기준으로 필터링하므로 더 적은 행을 읽습니다. 계산 속도 필터는 순서 지정 키의 유용한 접두사가 아닌 pickup_datetime, dropoff_datetime, trip_distance를 기반으로 하므로 여전히 전체 테이블을 읽습니다. EXPLAIN indexes = 1을 사용하여 날짜 범위 집계를 확인합니다:
ClickHouse 25.9 이상에서는 이러한 설정을 통해 EXPLAIN이 사용된 인덱스와 제외된 파트 및 그래뉼을 보고하도록 합니다.
프라이머리 인덱스는 40,167개의 그래뉼 중 5,061개를 선택합니다. 이로 인해 날짜 범위 집계는 전체 3억 2,904만 행이 아닌 4,146만 행만 처리합니다.

워크로드에 메서드 적용하기

자체 워크로드에도 다음 순서를 적용하십시오:
  1. 기준 기간, 읽은 행 수와 바이트 수, 피크 메모리를 기록합니다.
  2. 선택한 컬럼에 불필요하게 큰 범위이거나 지나치게 허용적인 타입이 사용되었는지 확인합니다.
  3. 데이터 레이아웃은 변경하지 않고 스키마 변경을 적용한 후 측정합니다.
  4. 중요한 반복 쿼리에서 사용하는 필터를 기반으로 순서 지정 키를 테스트합니다.
  5. EXPLAIN indexes = 1로 선택되는 데이터를 비교한 다음, 비교 가능한 조건에서 기준 쿼리를 다시 실행합니다.
이 예시의 타입이나 순서 지정 키가 다른 데이터셋에도 적합하다고 가정하지 마십시오. 관찰된 값과 쿼리 필터를 바탕으로 결정하십시오.

다음 단계

스키마 및 순서 지정 키를 변경해도 확인된 병목 현상이 해결되지 않으면 최적화 접근 방식으로 돌아가 프로젝션, materialized view, 데이터 스키핑 인덱스 또는 사전 계산을 검토하십시오.
마지막 수정일 2026년 8월 28일