system.query_log를 사용해 반복되는 느린 쿼리 패턴을 찾고, 대표적인 실행을 선택한 뒤 리소스 사용량을 검토하는 방법을 설명합니다. এরপর EXPLAIN을 사용해 쿼리 계획을 살펴보고, 쿼리 또는 스키마를 변경하기 전에 병목 지점에 대한 가설을 세웁니다.
시작하기 전에
nyc_taxi.trips_small_inferred 테이블을 사용합니다. 아직 테이블을 생성하고 로드하지 않았다면, 예시를 그대로 실행할 수 있도록 다음을 수행하십시오:
예시 데이터셋 설정
예시 데이터셋 설정
원본 Parquet 파일의 크기는 약 5.8 GB입니다. 네트워크 환경과 사용 가능한 리소스에 따라 로드하는 데 몇 분 정도 걸릴 수 있습니다.
SYSTEM FLUSH LOGS를 실행할 수 없는 경우 쿼리 로그가 자동으로 플러시될 때까지 기다린 후 첫 번째 조회를 다시 시도하십시오. 자체 워크로드를 진단할 때는 system.query_log에 확인하려는 시간 범위에 해당하는 완료된 실행 기록이 있는지 확인하십시오.
작동 방식
system.query_log 테이블에 기록합니다. 각 레코드에는 쿼리 실행 시간, 읽은 행 수, CPU 및 메모리 사용량, 파일 시스템 캐시 활동이 포함될 수 있습니다.
이러한 측정값을 통해 느린 쿼리 패턴을 파악하고 리소스 사용 방식을 이해할 수 있습니다. 대표적인 실행을 선택한 후에는 쿼리 계획을 살펴보고 쿼리에서 시간이 소요될 수 있는 지점을 조사할 수 있습니다.
클러스터에서는 쿼리 로그 데이터가 각 노드에 로컬로 유지됩니다. 이 가이드의 예시에서는 모든 레플리카를 쿼리하기 위해 clusterAllReplicas를 사용하고, 현재 system.query_log 테이블과 시스템 테이블 스키마 변경 후에도 유지되는 버전이 지정된 query_log_N 테이블을 포함하기 위해 merge를 사용합니다.
각 쿼리 로그 예시에는 클러스터형 배포와 단일 노드 배포를 위한 탭이 포함되어 있습니다. ClickHouse Cloud는 클러스터 예시에서 사용하는 default 클러스터를 제공합니다. 자가 관리형 배포에서는 default를 system.clusters에 나열된 클러스터로 바꾸십시오.
예시에서는 일시적으로 사용할 수 없는 레플리카 때문에 진단 쿼리가 실패하지 않도록
skip_unavailable_shards를 설정합니다. 이는 자동 스케일링 중에 특히 유용합니다. 건너뛴 레플리카의 레코드는 포함되지 않으므로 결과가 불완전할 수 있습니다.느린 쿼리 진단
후보 쿼리 식별
먼저 완료된 초기 쿼리를
normalized_query_hash 기준으로 그룹화합니다. 이렇게 하면 반복적으로 나타나는 쿼리 패턴과 개별적으로 느린 실행을 구분할 수 있습니다. 다음 쿼리는 중앙값 실행 시간을 기준으로 패턴의 순위를 매기고, 각 패턴에 대한 예시 쿼리를 함께 보여줍니다:- 클러스터
- 단일 노드
executions를 사용하면 반복적으로 실행되는 워크로드와 일회성 쿼리를 구분할 수 있습니다. 실행 시간 중앙값이 크거나, 실행 횟수가 잦거나, 리소스 사용량이 높은 패턴은 한 번 느리게 실행된 쿼리보다 조사 대상으로서 우선순위가 높습니다.간단히 현황을 파악하기 위해, 다음 쿼리는 NYC Taxi 데이터셋에 대해 서로 다른 최대 5개의 쿼리 패턴(query pattern) 각각에서 가장 느리게 완료된 실행을 나열합니다. 이때 데이터셋 로딩 SQL 문과 동일한 패턴의 반복 실행은 제외됩니다. 다음 단계에서는 위에서 선택한 normalized_query_hash에 해당하는 실행으로 쿼리 이력(query history)의 범위를 좁힙니다.- 클러스터
- 단일 노드
query_duration_ms 필드에는 쿼리 실행 시간이 밀리초 단위로 저장됩니다. 이 결과에서 가장 오래 실행된 쿼리는 2,967ms가 소요되었습니다.쿼리 실행 시간이 아닌 리소스 사용량을 기준으로 후보 쿼리를 찾을 수도 있습니다:리소스를 많이 사용하는 쿼리 찾기
리소스를 많이 사용하는 쿼리 찾기
이 쿼리는 최근 쿼리를 메모리 사용량순으로 정렬하고 각 쿼리의 CPU 사용량을 표시합니다. 결과는 워크로드와 배포 환경에 따라 달라집니다.
- 클러스터
- 단일 노드
대표 쿼리 실행을 선택합니다
느린 실행이 한 번 발생했다고 해서 반드시 문제가 있는 것은 아닙니다. 임시 쿼리나 일시적인 시스템 부하로 인한 이상치일 수 있습니다. 쿼리 계획을 살펴보기 전에 리터럴 값만 다른 쿼리에서 동일한 예시 쿼리 로그 결과를 보면 각 후보는 약 3억 2,904만 행을 읽었습니다. 참고로 예시 테이블의 행 수를 확인하십시오:테이블에는 3억 2,904만 개의 행이 있으며, 이는 각 후보의
normalized_query_hash를 기준으로 완료된 실행 여러 건을 검토하십시오. 해당 패턴의 일반적인 실행 시간과 리소스 사용량을 보여 주는 실행을 선택하십시오.selected_hash에 할당된 값을 조사하려는 패턴의 normalized_query_hash로 바꾸십시오:- 클러스터
- 단일 노드
read_rows와read_bytes가 비슷한 실행을 찾으십시오.- 해당 실행의
query_duration_ms와memory_usage를 비교하십시오. query_duration_ms가 중앙값에 가장 가까운query_id를 선택하십시오.
과거 쿼리 로그 결과는 캐시 상태와 시스템 부하에 따라 달라질 수 있으므로, 최적화 변경 사항을 비교하는 데 사용하지 말고 조사할 쿼리를 선택하는 데만 사용하십시오. 쿼리 로그에 완료된 실행이 충분히 없다면 비슷한 조건에서 쿼리를 몇 차례 실행하십시오. 다음 가이드인 쿼리 병목 현상 격리에서는 변경 사항을 비교하기 위해 통제된 측정값을 수집하는 방법을 설명합니다.
read_rows에 보고된 수와 거의 같습니다. 이는 쿼리가 테이블의 대부분 또는 전체를 스캔했음을 시사하지만, 해당 행을 읽은 이유나 이 정도의 읽기량이 쿼리에 적절한지는 알 수 없습니다. 다음으로 쿼리 계획을 확인하여 ClickHouse가 데이터를 선택하고 처리한 방식을 살펴보십시오.실행 계획 확인
대표적인 실행을 선택한 후 출력에는 다음 작업이 포함됩니다. 파트 및 그래뉼 수와 같은 세부 정보는 데이터 저장 방식에 따라 달라집니다.아래에서 위로 살펴보면 계획은 쿼리의 각 부분과 다음과 같이 대응됩니다:
EXPLAIN을 사용하여 쿼리를 실행하지 않고 ClickHouse가 쿼리 실행을 어떻게 계획하는지 확인합니다. 출력에는 ClickHouse가 수행할 것으로 예상하는 작업과 작업 간 데이터 이동 방식이 표시되어 쿼리 로그의 측정값을 더 잘 이해하는 데 도움이 됩니다.사용 가능한 출력 형식에 관한 자세한 내용은 분석기를 사용하여 쿼리 실행 이해하기를 참조하십시오. 이 예시에서 EXPLAIN은 ClickHouse가 데이터를 읽고 필터링하는 방식을 어떻게 계획하는지와 일부 데이터를 건너뛸 수 있는지를 보여줍니다.출력은 ClickHouse가 데이터를 읽고, 필터링하고, 처리할 것으로 예상하는 방식을 보여주는 작업 트리입니다. 하위 작업은 상위 작업 아래에 표시됩니다. 가장 깊은 읽기 작업부터 시작한 다음 계획을 위로 따라가며 ClickHouse가 데이터를 최종 결과로 변환하는 방식을 확인합니다.이 예시에서는 쿼리 로그 결과에서 계산된 속도 쿼리를 살펴봅니다:ReadFromMergeTree는nyc_taxi.trips_small_inferred에서 데이터를 읽습니다.Indexes섹션이 없고read_rows가 테이블의 행 수와 일치하므로, ClickHouse가 테이블 전체를 읽고 있음을 알 수 있습니다.Filter는speed_mph > 30의 확장된 표현식을 보여줍니다. ClickHouse는 읽은 모든 행에 대해 운행 시간과 속도를 계산한 후 시속 30마일을 초과하는 행만 유지합니다.Aggregating은 필터링된trip_distance값의 분위수를 계산합니다.
speed_mph 계산, 분위수 계산입니다.