시작하기 전에
nyc_taxi.trips_small_inferred 테이블을 사용합니다. 예시를 그대로 실행하려면 아직 테이블을 생성하고 로드하지 않은 경우 다음을 수행하십시오.
예시 데이터셋 설정
예시 데이터셋 설정
원본 Parquet 파일의 크기는 약 5.8 GB입니다. 네트워크 환경과 사용 가능한 리소스에 따라 로드하는 데 몇 분 정도 걸릴 수 있습니다.
접근 방식 선택
근거가 이러한 카테고리 중 어느 하나에도 해당하지 않으면, 쿼리를 특정 접근 방식에 억지로 맞추기보다 쿼리 계획으로 돌아가십시오.
읽는 데이터 줄이기
- 사용 시점: 쿼리가 넓은 컬럼이나 필요하지 않은 컬럼을 읽을 때입니다.
- 변경: 쿼리에서 읽는 컬럼의 크기나 개수를 줄입니다.
- 검증: 동일한 조건에서
read_bytes, 메모리 사용량, 소요 시간을 비교합니다.
컬럼 타입 검토
String 대신 숫자 및 날짜 타입을 사용하고, 예상 범위를 안전하게 표현할 수 있는 가장 작은 부호 있는 또는 부호 없는 숫자 타입을 선택하십시오. 시간 관련 컬럼에는 Date32 또는 DateTime64의 더 넓은 범위나 소수점 이하 정밀도가 필요하지 않은 한 Date 또는 DateTime를 사용하십시오.
널 허용 컬럼을 신중하게 사용
Nullable 컬럼은 값과 별도로 null 마스크를 저장하며, ClickHouse는 이 마스크도 읽고 처리해야 합니다. null 값과 타입의 기본값을 구분하는 것이 중요할 때 사용하십시오. 컬럼에 항상 값이 포함되는 것이 보장된다면 널 비허용 타입을 사용해 이러한 추가 작업을 피할 수 있습니다.
컬럼을 변경하기 전에 관찰된 non-null 데이터가 항상 non-null 상태로 유지된다고 가정하지 말고 소스 데이터와 수집 경로를 확인하십시오. 최적화 예시에서는 null 값을 포함하는 컬럼을 식별하고 스키마 변경의 영향을 측정하는 방법을 보여 줍니다.
반복되는 값에 딕셔너리 인코딩 사용
LowCardinality는 딕셔너리 인코딩을 사용하며, 상태 값, 국가 코드 또는 행 수에 비해 고유 값 수가 현저히 적은 기타 차원과 같은 문자열 컬럼에 효과적인 경우가 많습니다. 약 10,000개의 고유 값은 후보를 식별하는 유용한 시작점일 뿐, 고정된 기준은 아닙니다. 식별자와 대부분의 값이 고유한 기타 컬럼은 피하고, 타입 변경 전후의 측정값을 비교하십시오.
더 자세한 지침은 데이터 타입 선택을 참조하십시오.
필요한 컬럼만 읽기
SELECT * 대신 필요한 컬럼을 명시하십시오.
system.query_log의 read_bytes를 사용하여 선택 컬럼을 줄이기 전후의 데이터 읽기 양을 비교하십시오. read_bytes가 여전히 높다면 추가 컬럼이 필요한 표현식, 필터, 조인 또는 중첩 쿼리가 있는지 쿼리 계획을 확인하십시오.
예를 들어 대시보드에 픽업 시간, 결제 유형, 총액만 필요하다면 전체 행 대신 해당 컬럼만 선택하십시오:
SELECT * 쿼리와 비교하십시오. 반환되는 행 수는 같지만, read_bytes에는 더 적은 컬럼을 읽은 결과가 반영되어야 합니다.
데이터 레이아웃을 쿼리에 맞추기
- 사용 시점: 선택적 필터를 적용해도 많은 파트 또는 그래뉼을 읽는 경우입니다.
- 변경: 반복적으로 실행되는 쿼리의 필터에 맞게 물리적 레이아웃을 조정합니다.
- 검증:
EXPLAIN indexes = 1로 선택되는 파트와 그래뉼을 비교한 후read_rows,read_bytes, 소요 시간을 확인합니다.
순서 지정 키부터 시작하기
MergeTree 엔진 계열 테이블에서 순서 지정 키는 행이 디스크에 정렬되는 방식을 결정합니다. 기본적으로 이 키는 희소 프라이머리 인덱스를 정의하는 프라이머리 키 역할도 합니다. OLTP 데이터베이스의 프라이머리 키와 달리 ClickHouse 프라이머리 키는 고유성을 강제하지 않습니다. 대신 쿼리 필터 조건을 충족할 수 없는 그래뉼을 ClickHouse가 건너뛸 수 있도록 하여 성능을 향상합니다.
키 내 컬럼의 순서도 고려하여, 선택성이 높은 필터에 자주 사용되는 컬럼을 우선 배치하십시오. 관련된 값을 함께 그룹화하면 압축률도 향상될 수 있습니다. 쿼리의 그룹화 또는 정렬 순서가 키와 일치하면 ClickHouse는 GROUP BY 또는 ORDER BY에 순서 기반 최적화를 사용할 수 있습니다.
다른 순서 지정 키를 테스트하기 전후로 EXPLAIN indexes = 1에서 선택된 파트와 그래뉼을 비교하십시오. 또한 동일한 조건에서 read_rows, read_bytes, 소요 시간을 비교하십시오. 자세한 선택 지침은 프라이머리 키 선택을 참조하십시오.
예시 테이블은 ORDER BY ()를 사용하므로, 다음 선택적 날짜 필터에서 그래뉼을 제외할 수 있는 순서 지정 키가 없습니다:
ClickHouse 25.9 이상에서는 이러한 설정을 통해
EXPLAIN에서 사용된 인덱스와 제외된 파트 및 그래뉼을 확인할 수 있습니다.pickup_datetime를 포함하는 순서 지정 키가 있는 테이블을 만든 후, 동일한 EXPLAIN을 실행하십시오. 전체 변경 사항을 평가하기 위해 Duration 또는 메모리 측정값을 사용하기 전에 계획의 프라이머리 키 섹션에서 선택된 그래뉼 수가 더 적게 표시되어야 합니다.
추가 인덱싱 및 데이터 레이아웃 옵션 평가
EXPLAIN indexes = 1을 사용하여 쿼리가 실제로 파티션을 프루닝하는지 확인하십시오.
국소화된 필터에 데이터 스키핑 인덱스 추가
데이터 스키핑 인덱스는 ClickHouse가 필터와 일치할 수 없는 블록을 읽지 않도록 하는 메타데이터를 저장합니다. 순서 지정 키가 중요한 필터를 지원하지 않고, 일치하는 값이 블록 내에 충분히 국소화되어 있을 때 가장 유용합니다.
예를 들어 대부분의 블록에 검색 대상 값이 포함되지 않는다면 블룸 필터 인덱스가 동등 조회에 도움이 될 수 있습니다. 데이터 타입과 순서 지정 키를 검토한 후 스키핑 인덱스를 사용하십시오. 블록을 거의 제외하지 못하는 인덱스는 작업량을 크게 줄이지 못하면서 스토리지와 평가 오버헤드를 추가합니다. 대표 데이터로 인덱스 유형과 세분화 수준을 테스트한 다음, EXPLAIN indexes = 1을 사용하여 선택된 그래뉼을 비교하고 read_rows, read_bytes, 소요 시간을 확인하십시오.
프로젝션을 선택적으로 사용
프로젝션은 테이블과 함께 대체 데이터 레이아웃을 저장합니다. 다른 순서 지정 키나 사전 계산된 결과를 제공할 수 있으며, ClickHouse는 쿼리에서 직접 참조하지 않아도 적용 가능한 프로젝션을 선택할 수 있습니다.
예를 들어 payment_type으로 정렬된 프로젝션은 기본 테이블’의 정렬로 지원되지 않는 반복적인 필터를 지원할 수 있습니다. 기본 정렬로 효율적으로 처리할 수 없는 중요한 액세스 패턴에는 소수의 프로젝션을 사용하십시오.
프로젝션은 추가 인덱스 또는 컬럼 데이터를 저장하며, 삽입과 병합 시 작업량을 늘립니다. 전체 컬럼 프로젝션은 저장하는 컬럼을 복제합니다. 프로젝션을 과도하게 사용하면 쿼리 시점에 최적의 프로젝션을 선택하는 데 필요한 작업량도 늘어날 수 있습니다. 서로 다른 액세스 패턴이 많은 대규모 배포에서는 프로젝션 수를 줄이거나 용도별 테이블을 별도로 사용하는 편이 운영하기 쉬운 경우가 많습니다. 이러한 메커니즘 중 하나를 선택할 때는 materialized view와 프로젝션 비교를 참조하십시오.
원본 테이블을 계속 쿼리하면서 결제 유형과 픽업 시간으로 필터링하는 쿼리를 위한 대체 정렬을 추가합니다:
EXPLAIN projections = 1을 사용하여 ClickHouse가 프로젝션을 선택하는지, 더 적은 행 또는 바이트를 읽는지 확인하십시오. 또한 이 패턴을 광범위하게 적용하기 전에 삽입 및 스토리지 오버헤드를 측정하십시오.
반복 작업 사전 계산
- 사용 시점: 동일한 변환이나 집계가 반복적으로 쿼리 시간의 대부분을 차지할 때입니다.
- 변경: 반복되는 계산을 수집 시점, 예약된 갱신 또는 용도에 맞게 설계된 데이터 레이아웃으로 옮깁니다.
- 검증: 쿼리가 더 작은 결과를 읽고 쿼리 시점의 계산량이 줄어드는지, 수집 또는 갱신 작업은 허용 가능한 수준인지 확인합니다.
각 섹션에는 기본 구현 방법, 주요 운영상 절충 관계 및 결과 검증 방법이 포함되어 있습니다.
증분형 materialized view
pickup_date로 그룹화하여 sum(trip_count)를 쿼리하면 백그라운드 머지를 기다리는 행이 쿼리 시점에 결합됩니다. 이 뷰는 새로 삽입된 데이터만 처리하므로 기존 소스 데이터는 별도로 백필하십시오. 소요 시간과 읽은 행 수를 원래 집계와 비교해 변경 사항을 검증한 후, 추가 삽입 작업이 허용 가능한 수준인지 확인하십시오.
갱신 가능 구체화 뷰
system.view_refreshes를 확인하여 갱신 소요 시간, 상태 및 빈도가 워크로드에 적합한지 확인합니다.
용도에 맞게 설계된 테이블
Nullable을 제거합니다. 이러한 처리가 워크로드의 데이터 요구 사항에 맞는지 확인하십시오. 대시보드는 이 테이블을 명시적으로 쿼리해야 하며, 수집 파이프라인은 테이블을 최신 상태로 유지해야 합니다. 행 수와 읽은 바이트 수, 메모리 사용량, 소요 시간을 소스 테이블 쿼리와 비교하여 변경 사항을 검증하십시오. 의사 결정 시 추가 스토리지와 파이프라인 유지 관리 비용도 고려하십시오.