쿼리 계획 및 타임라인

쿼리 작업 내에 삽입된 BigQuery에는 진단 쿼리 계획과 타이밍 정보가 포함되어 있습니다. 이는 다른 데이터베이스 및 분석 시스템에서 EXPLAIN과 같은 문으로 제공되는 정보와 유사합니다. 이 정보는 jobs.get과 같은 메서드의 API 응답에서 가져올 수 있습니다.

장기간 실행 중인 쿼리의 경우 BigQuery는 통계를 주기적으로 업데이트합니다. 이러한 업데이트는 작업 상태가 폴링되는 비율과 상관없이 진행되지만 일반적으로 최소 주기는 30초입니다. 또한 실행 리소스를 사용하지 않는 쿼리 작업(예: 테스트 실행 요청 또는 캐시된 결과에서 제공할 수 있는 결과)에는 추가 진단 정보가 포함되지 않지만 다른 통계가 포함될 수 있습니다.

배경

BigQuery는 쿼리 작업 실행 시 선언적 SQL 문을 실행 그래프로 변환하고 일련의 쿼리 스테이지로 세분화합니다. 각 쿼리 스테이지에는 더 세분화된 실행 단계 조합이 있습니다. BigQuery는 크게 분산된 병렬 아키텍처를 사용하여 이러한 쿼리를 실행하며, 스테이지는 많은 잠재적 작업자들이 동시에 실행할 수 있는 작업 단위를 모델링합니다. 스테이지는 빠른 분산 셔플 아키텍처를 사용하여 서로 통신합니다.

쿼리 계획 내에서 작업 단위작업자라는 용어는 특히 동시 로드에 대한 정보를 전달하기 위해 사용됩니다. BigQuery 내의 다른 곳에서는 컴퓨팅, 메모리, I/O 리소스를 비롯한 쿼리 실행의 여러 측면을 추상화한 '슬롯'이라는 용어가 사용될 수 있습니다. 최상위 작업 통계는 이 추상화된 계정을 통해 쿼리의 totalSlotMs 추정을 사용함으로써 개별 쿼리 비용의 추정치를 제공합니다.

쿼리 실행 아키텍처의 또 다른 중요한 속성은 동적이라는 것입니다. 즉, 쿼리가 실행되는 동안 쿼리 계획이 수정될 수 있습니다. 쿼리가 실행 중인 동안에 스테이지를 도입하는 목적은 쿼리 작업자 전체의 데이터 배포를 향상시키기 위한 경우가 많습니다. 실제로 이러한 쿼리 계획에서 스테이지에는 일반적으로 리파티션 스테이지라는 라벨이 지정됩니다.

쿼리 계획 외에도 쿼리 작업에는 쿼리 작업자 사이에서 완료, 보류, 활성 상태의 작업 단위에 대한 설명을 제공하는 실행 타임라인이 노출됩니다. 쿼리에는 활성 작업자를 포함한 여러 스테이지가 동시에 있을 수 있으므로, 타임라인은 쿼리의 전반적인 진행 상황을 보여주기 위한 것입니다.

Google Cloud 콘솔에서 정보 보기

Google Cloud 콘솔에서 실행 세부정보 버튼(결과 버튼 근처)을 클릭하여 완료된 쿼리의 쿼리 계획 세부정보를 볼 수 있습니다.

쿼리 계획

쿼리 계획 정보

API 응답 내에서 쿼리 계획은 쿼리 스테이지 목록으로 표시됩니다. 목록의 각 항목은 스테이지별 개요 통계, 세부 단계 정보, 스테이지 타이밍 분류를 보여줍니다. Google Cloud 콘솔 내부에서 모든 세부정보가 렌더링되지는 않지만 API 응답 내에 모든 정보가 있을 수 있습니다.

스테이지 개요

각 스테이지의 개요 필드에 포함될 수 있는 항목은 다음과 같습니다.

API 필드 설명
id 스테이지의 고유 숫자 ID입니다.
name 스테이지의 간단한 요약 이름입니다. 스테이지 내의 steps는 실행 단계에 대한 추가 세부정보를 제공합니다.
status 스테이지의 실행 상태입니다. 가능한 상태로는 PENDING, RUNNING, COMPLETE, FAILED, CANCELLED가 있습니다.
inputStages 스테이지의 종속성 그래프를 구성하는 ID 목록입니다. 예를 들어 JOIN 스테이지에는 주로 JOIN 관계의 왼쪽과 오른쪽에서 데이터를 준비하는 독립 스테이지 2개가 필요합니다.
startMs 스테이지 내에서 첫 작업자가 실행을 시작한 시간을 나타내는 이폭 밀리초 단위의 타임스탬프입니다.
endMs 마지막 작업자가 실행을 완료한 시간을 나타내는 이폭 밀리초 단위의 타임스탬프입니다.
steps 스테이지 내의 세부적인 실행 단계 목록입니다. 자세한 내용은 다음 섹션을 참조하세요.
recordsRead 모든 스테이지 작업자에 대한 레코드 수 기준의 스테이지 입력 크기입니다.
recordsWritten 모든 스테이지 작업자에 대한 레코드 수 기준의 스테이지 출력 크기입니다.
parallelInputs 스테이지에서 동시 로드가 가능한 작업 단위 수입니다. 스테이지 및 쿼리에 따라 테이블 내의 열 형식 세그먼트 수 또는 중간 셔플 내의 파티션 수수를 나타낼 수 있습니다.
completedParallelInputs 완료된 스테이지 내의 작업 단위 수입니다. 일부 쿼리의 경우 스테이지 내 일부 입력을 완료하지 않아도 스테이지가 완료될 수 있습니다.
shuffleOutputBytes 쿼리 스테이지 내 모든 작업자에 대한 총 바이트 수를 나타냅니다.
shuffleOutputBytesSpilled 스테이지 간에 중요한 데이터를 전송하는 쿼리를 디스크 기반 전송으로 대체해야 할 수도 있습니다. 분산된 바이트 통계는 디스크로 분산된 데이터의 양을 알려줍니다. 이는 최적화 알고리즘에 따라 다르므로 특정 쿼리에 대해 확정적이지 않습니다.

스테이지별 타이밍 분류

쿼리 스테이지는 상대적 형태 및 절대적 형태로 스테이지 타이밍 분류를 제공합니다. 각 실행 스테이지는 독립 작업자 1명 이상이 수행한 작업을 나타내므로, 평균 시간과 가장 느린 시간으로 정보가 제공됩니다. 즉, 스테이지의 모든 작업자에 대한 평균 성과와 지정된 분류에서 가장 시간이 오래 걸린 롱테일 작업자의 성과가 표시됩니다. 평균 및 최대 시간은 절대 표시와 상대 표시로 더 세분화됩니다. 비율 기반 통계의 경우 임의의 세그먼트에 있는 작업자 중 가장 시간이 오래 걸린 작업자 시간의 비율로 데이터가 제공됩니다.

Google Cloud 콘솔은 상대적 타이밍 표시를 사용하여 스테이지 타이밍을 나타냅니다.

스테이지 타이밍 정보는 다음과 같이 보고됩니다.

상대적 타이밍 절대적 타이밍 비율 분자
waitRatioAvg waitMsAvg 평균 작업자가 예약되기 위해 대기한 시간입니다.
waitRatioMax waitMsMax 가장 시간이 오래 걸린 작업자가 예약되기 위해 대기한 시간입니다.
readRatioAvg readMsAvg 평균 작업자가 입력 데이터를 읽는 데 걸린 시간입니다.
readRatioMax readMsMax 가장 시간이 오래 걸린 작업자가 입력 데이터를 읽는 데 걸린 시간입니다.
computeRatioAvg computeMsAvg 평균 작업자가 CPU의 제한을 받은 시간입니다.
computeRatioMax computeMsMax 가장 시간이 오래 걸린 작업자가 CPU의 제한을 받은 시간입니다.
writeRatioAvg writeMsAvg 평균 작업자가 출력 데이터를 쓰는 데 걸린 시간입니다.
writeRatioMax writeMsMax 가장 시간이 오래 걸린 작업자가 출력 데이터를 쓰는 데 걸린 시간입니다.

단계 개요

단계에는 스테이지 내의 각 작업자가 실행하는 작업이 포함되며 순서가 지정된 작업 목록으로 제공됩니다. 각 단계 작업에는 카테고리가 있으며 일부 작업은 보다 세부적인 정보를 제공합니다. 쿼리 계획의 작업 카테고리는 다음과 같습니다.

단계 카테고리 설명
READ 입력 테이블 또는 중간 셔플에서 열 한 개 이상 읽기. 읽은 처음 16개 열만 단계 세부정보에서 반환됩니다.
WRITE 출력 테이블 또는 중간 셔플에 열을 하나 이상 쓰기, 스테이지에서 HASH 파티션을 나눈 출력의 경우, 파티션 키에 사용된 열도 포함됩니다.
COMPUTE 표현식 평가 및 SQL 함수
FILTER WHERE, OMIT IF, HAVING 절에서 사용합니다.
SORT 열 키와 정렬 순서가 포함된 ORDER BY 연산
AGGREGATE GROUP BY 또는 COUNT와 같은 절의 집계를 구현합니다.
LIMIT LIMIT 절을 구현합니다.
JOIN JOIN와 같은 절의 조인을 구현합니다. 조인 유형과 조인 조건이 포함될 수 있습니다.
ANALYTIC_FUNCTION 윈도우 함수 호출('분석 함수'라고도 함)
USER_DEFINED_FUNCTION 사용자 정의 함수 호출

단계 해석 및 최적화

다음 섹션에서는 쿼리 계획의 단계를 해석하는 방법을 설명하고 쿼리를 최적화하는 방법을 제공합니다.

READ 단계

READ 단계는 단계에서 처리를 위해 데이터에 액세스하고 있음을 의미합니다. 데이터는 쿼리에서 참조된 테이블 또는 셔플 메모리에서 직접 읽을 수 있습니다. 이전 단계의 데이터가 읽히면 BigQuery는 셔플 메모리에서 데이터를 읽습니다. 스캔되는 데이터의 양은 주문형 슬롯을 사용할 때는 비용에 영향을 미치고 예약을 사용할 때는 성능에 영향을 미칩니다.

잠재적인 성능 문제

  • 파티션되지 않은 테이블의 대규모 스캔: 쿼리에 데이터의 일부만 필요한 경우 테이블 스캔이 비효율적일 수 있습니다. 파티셔닝이 좋은 최적화 전략이 될 수 있습니다.
  • 필터 비율이 낮은 대규모 테이블 스캔: 필터가 스캔되는 데이터를 효과적으로 줄이지 못하고 있음을 나타냅니다. 필터 조건을 수정해 보세요.
  • 디스크에 셔플 바이트가 스필 오버됨: 클러스터에서 유사한 데이터를 유지할 수 있는 클러스터링과 같은 최적화 기법을 사용하여 데이터가 효과적으로 저장되지 않았음을 나타냅니다.

최적화

  • 타겟팅 필터링: WHERE 절을 전략적으로 사용하여 쿼리에서 최대한 빨리 관련 없는 데이터를 필터링합니다. 이렇게 하면 쿼리에서 처리해야 하는 데이터 양이 줄어듭니다.
  • 파티션 나누기 및 클러스터링: BigQuery는 테이블 파티션 나누기 및 클러스터링을 사용하여 특정 데이터 세그먼트를 효율적으로 찾습니다. READ 단계에서 스캔되는 데이터를 최소화하려면 일반적인 쿼리 패턴에 따라 테이블을 파티션하고 클러스터링해야 합니다.
  • 관련 열 선택: SELECT * 문을 사용하지 마세요. 대신 특정 열을 선택하거나 SELECT * EXCEPT를 사용하여 불필요한 데이터를 읽지 마세요.
  • 구체화된 뷰: 구체화된 뷰는 자주 사용되는 집계를 미리 계산하고 저장할 수 있으므로 이러한 뷰를 사용하는 쿼리의 READ 단계에서 기본 테이블을 읽을 필요가 줄어들 수 있습니다.

COMPUTE 단계

COMPUTE 단계에서 BigQuery는 데이터에 대해 다음 작업을 실행합니다.

  • 계산, 비교, 논리 연산을 비롯하여 쿼리의 SELECT, WHERE, HAVING 및 기타 절의 표현식을 평가합니다.
  • 내장 SQL 함수와 사용자 정의 함수를 실행합니다.
  • 쿼리의 조건에 따라 데이터 행을 필터링합니다.

최적화

쿼리 계획을 통해 COMPUTE 단계 내의 병목 현상을 파악할 수 있습니다. 계산이 많거나 처리되는 행 수가 많은 단계를 찾습니다.

  • COMPUTE 단계와 데이터 양의 연관성 비교: 한 단계에서 상당한 계산이 이루어지고 대량의 데이터가 처리되는 경우 최적화의 좋은 후보가 될 수 있습니다.
  • 왜곡된 데이터: 연산 최대값이 연산 평균보다 훨씬 높은 단계의 경우, 해당 단계에서 데이터 슬라이스를 몇 개 처리하는 데 불균형하게 많은 시간을 소비했음을 나타냅니다. 데이터 분포를 확인하여 데이터 편향이 있는지 살펴보는 것이 좋습니다.
  • 데이터 유형 고려: 열에 적절한 데이터 유형을 사용합니다. 예를 들어 문자열 대신 정수, 날짜-시간, 타임스탬프를 사용하면 성능을 개선할 수 있습니다.

WRITE 단계

WRITE 단계는 중간 데이터와 최종 출력에 대해 실행됩니다.

  • 셔플 메모리에 쓰기: 다단계 쿼리에서 WRITE 단계는 종종 추가 처리를 위해 처리된 데이터를 다른 단계로 전송하는 것을 포함합니다. 이는 여러 소스의 데이터를 결합하거나 집계하는 셔플 메모리의 일반적인 사례입니다. 이 단계에서 작성된 데이터는 일반적으로 최종 출력이 아닌 중간 결과입니다.
  • 최종 출력: 쿼리 결과가 대상 테이블 또는 임시 테이블에 쓰여집니다.

HASH 파티션 나누기

쿼리 계획의 스테이지가 해시 파티션된 출력에 데이터를 쓰면 BigQuery는 출력에 포함된 열과 파티션 키로 선택된 열을 씁니다.

최적화

WRITE 단계 자체는 직접 최적화되지 않을 수 있지만, 이 단계의 역할을 이해하면 초기 단계에서 잠재적인 병목 현상을 파악하는 데 도움이 됩니다.

  • 쓰기 데이터 최소화: 필터링 및 집계를 사용하여 이전 단계를 최적화하여 이 단계에서 쓰는 데이터의 양을 줄이는 데 집중합니다.
  • 파티션: 쓰기에 테이블 파티션이 큰 도움이 됩니다. 쓰는 데이터가 특정 파티션으로 제한되는 경우 BigQuery에서 더 빠르게 쓸 수 있습니다.

    DML 문에 테이블 파티션 열에 대한 정적 조건이 있는 WHERE 절이 있는 경우 BigQuery는 관련 테이블 파티션만 수정합니다.

  • 비정규화 절충사항: 비정규화로 인해 중간 WRITE 단계에서 결과 집합이 더 작아질 수 있습니다. 그러나 스토리지 사용량 증가 및 데이터 일관성 문제와 같은 단점이 있습니다.

JOIN 단계

JOIN 단계에서 BigQuery는 두 데이터 소스의 데이터를 결합합니다. 조인에는 조인 조건이 포함될 수 있습니다. 조인은 리소스를 많이 사용합니다. BigQuery에서 대규모 데이터를 조인할 때는 조인 키가 독립적으로 셔플되어 동일한 슬롯에 정렬되므로 각 슬롯에서 로컬로 조인이 실행됩니다.

JOIN 단계의 쿼리 계획에는 일반적으로 다음 세부정보가 표시됩니다.

  • 조인 패턴: 사용된 조인 유형을 나타냅니다. 각 유형은 조인된 테이블의 행이 결과 집합에 포함되는 수를 정의합니다.
  • 조인 열: 데이터 소스 간에 행을 일치시키는 데 사용되는 열입니다. 열 선택은 조인 성능에 매우 중요합니다.

조인 패턴

  • 브로드캐스트 조인: 하나의 테이블(일반적으로 더 작은 테이블)이 단일 작업자 노드 또는 슬롯의 메모리에 들어갈 수 있는 경우 BigQuery는 이 테이블을 다른 모든 노드로 브로드캐스트하여 조인을 효율적으로 실행할 수 있습니다. 단계 세부정보에서 JOIN EACH WITH ALL을 찾습니다.
  • 해시 조인: 테이블이 크거나 브로드캐스트 조인이 적합하지 않은 경우 해시 조인이 사용될 수 있습니다. BigQuery는 해시 및 셔플 작업을 사용하여 왼쪽 및 오른쪽 테이블을 셔플하여 일치하는 키가 동일한 슬롯에 들어가 로컬 조인을 실행합니다. 해시 조인은 데이터를 이동해야 하므로 비용이 많이 드는 작업이지만 해시 간에 행을 효율적으로 일치시킬 수 있습니다. 단계 세부정보에서 JOIN EACH WITH EACH를 찾습니다.
  • 자체 조인: 테이블이 자체적으로 조인되는 SQL 안티패턴입니다.
  • 교차 조인: 입력보다 더 큰 출력 데이터를 생성하므로 심각한 성능 문제를 일으킬 수 있는 SQL 안티패턴입니다.
  • 왜곡된 조인: 한 테이블의 조인 키에 걸쳐 데이터가 매우 왜곡되어 성능 문제가 발생할 수 있습니다. 최대 컴퓨팅 시간이 쿼리 계획의 평균 컴퓨팅 시간보다 훨씬 더 긴 사례를 찾습니다. 자세한 내용은 카디널리티가 높은 조인파티션 편향을 참고하세요.

디버깅

  • 대규모 데이터: 쿼리 계획에 JOIN 단계에서 처리된 상당한 양의 데이터가 표시되면 조인 조건과 조인 키를 조사합니다. 더 선택적인 조인 키를 필터링하거나 사용하는 것이 좋습니다.
  • 편향된 데이터 분포: 조인 키의 데이터 분포를 분석합니다. 한 테이블이 매우 왜곡된 경우 쿼리 분할 또는 사전 필터링과 같은 전략을 살펴보세요.
  • 카디널리티가 높은 조인: 왼쪽 및 오른쪽 입력 행 수보다 훨씬 더 많은 행을 생성하는 조인은 쿼리 성능을 크게 저하시킬 수 있습니다. 행이 매우 많이 생성되는 조인은 피하세요.
  • 테이블 순서가 잘못됨: INNER 또는 LEFT와 같은 적절한 조인 유형을 선택하고 쿼리의 요구사항에 따라 테이블을 가장 큰 순서에서 가장 작은 순서로 정렬했는지 확인합니다.

최적화

  • 선택적 조인 키: 조인 키의 경우 가능하면 STRING 대신 INT64를 사용하세요. STRING 비교는 문자열의 각 문자를 비교하므로 INT64 비교보다 느립니다. 정수에는 단일 비교만 필요합니다.
  • 조인 전 필터링: 조인하기 전에 개별 테이블에 WHERE 절 필터를 적용합니다. 이렇게 하면 조인 작업에 관련된 데이터의 양이 줄어듭니다.
  • 조인 열의 함수 피하기: 조인 열에서 함수를 호출하지 않습니다. 대신 ELT SQL 파이프라인을 사용하여 처리 또는 처리 후 프로세스 중에 테이블 데이터를 표준화합니다. 이 접근 방식을 사용하면 조인 열을 동적으로 수정할 필요가 없으므로 데이터 무결성을 손상시키지 않고도 더 효율적인 조인을 사용할 수 있습니다.
  • 자체 조인 방지: 자체 조인은 일반적으로 행 종속 관계를 계산하는 데 사용됩니다. 하지만 자체 조인을 사용하면 출력 행 수가 4배로 늘어나 성능 문제가 발생할 수 있습니다. 자체 조인을 사용하는 대신 윈도우 (분석) 함수를 사용하는 것이 좋습니다.
  • 큰 테이블 먼저: SQL 쿼리 최적화 도구가 어느 테이블이 조인의 어느 쪽으로 가야 하는지를 결정할 수 있지만 조인된 테이블을 적절히 정렬합니다. 권장사항은 가장 큰 테이블을 가장 먼저 배치하고 가장 작은 테이블을 그 뒤에 둔 다음 점점 크기를 줄여나가는 것입니다.
  • 비정규화: 경우에 따라 테이블을 전략적으로 비정규화(중복 데이터 추가)하면 조인을 완전히 제거할 수 있습니다. 그러나 이 접근 방식에는 저장소 및 데이터 일관성의 절충점이 있습니다.
  • 파티션 나누기 및 클러스터링: 조인 키를 기반으로 테이블을 파티션화하고 함께 배치된 데이터를 클러스터링하면 BigQuery가 관련 데이터 파티션을 타겟팅하도록 허용하여 조인 속도를 크게 높일 수 있습니다.
  • 편향된 조인 최적화: 편향된 조인과 관련된 성능 문제를 방지하려면 최대한 빨리 테이블의 데이터를 사전 필터링하거나 쿼리를 두 개 이상의 쿼리로 분할합니다.

AGGREGATE 단계

AGGREGATE 단계에서 BigQuery는 데이터를 집계하고 그룹화합니다.

디버깅

  • 단계 세부정보: 집계의 입력 행 수와 출력 행 수, 셔플 크기를 확인하여 집계 단계에서 달성한 데이터 감소량과 데이터 셔플이 사용되었는지 확인합니다.
  • 셔플 크기: 셔플 크기가 클수록 집계 중에 워커 노드 간에 상당한 양의 데이터가 이동했음을 나타낼 수 있습니다.
  • 데이터 분포 확인: 데이터가 파티션에 균등하게 분산되어 있는지 확인합니다. 데이터 분포가 왜곡되면 집계 단계에서 워크로드가 불균형해질 수 있습니다.
  • 집계 검토: 집계 절을 분석하여 집계가 필요하고 효율적인지 확인합니다.

최적화

  • 클러스터링: GROUP BY, COUNT 또는 기타 집계 절에서 자주 사용되는 열을 기준으로 테이블을 클러스터링합니다.
  • 분할: 쿼리 패턴에 맞는 분할 전략을 선택합니다. 집계 중에 스캔되는 데이터의 양을 줄이기 위해 수집 시간으로 파티션을 나눈 테이블을 사용하는 것이 좋습니다.
  • 조기 집계: 가능하면 쿼리 파이프라인 초기에 집계를 실행합니다. 이렇게 하면 집계 중에 처리해야 하는 데이터의 양을 줄일 수 있습니다.
  • 셔플 최적화: 셔플이 병목 현상인 경우 이를 최소화하는 방법을 모색합니다. 예를 들어 테이블을 비정규화하거나 클러스터링을 사용하여 관련 데이터를 배치합니다.

특이 사례

  • DISTINCT 집계: DISTINCT 집계가 포함된 쿼리는 특히 대규모 데이터 세트에서 계산 비용이 많이 들 수 있습니다. 대략적인 결과를 얻으려면 APPROX_COUNT_DISTINCT와 같은 대안을 고려하세요.
  • 대규모 그룹: 쿼리에서 대규모 그룹이 생성되면 상당한 양의 메모리가 사용될 수 있습니다. 이 경우 그룹 수를 제한하거나 다른 집계 전략을 사용하는 것이 좋습니다.

REPARTITION 단계

REPARTITIONCOALESCE는 모두 BigQuery가 쿼리의 셔플된 데이터에 직접 적용하는 최적화 기법입니다.

  • REPARTITION: 이 작업은 작업자 노드 간에 데이터 분포를 재조정하는 것을 목표로 합니다. 셔플 후 한 워커 노드에 비례하지 않게 많은 양의 데이터가 포함된다고 가정해 보겠습니다. REPARTITION 단계에서는 데이터를 더 균등하게 재분배하여 단일 작업자가 병목 현상을 일으키지 않도록 합니다. 이는 조인과 같이 계산 집약적인 작업에 특히 중요합니다.
  • COALESCE: 이 단계는 셔플 후 데이터의 작은 버킷이 많을 때 실행됩니다. COALESCE 단계에서는 이러한 버킷을 더 큰 버킷으로 결합하여 여러 소규모 데이터를 관리하는 데 드는 오버헤드를 줄입니다. 이는 매우 작은 중간 결과 집합을 처리할 때 특히 유용합니다.

쿼리 계획에 REPARTITION 또는 COALESCE 단계가 표시된다고 해서 반드시 쿼리에 문제가 있는 것은 아닙니다. 이는 BigQuery에서 성능 향상을 위해 데이터 배포를 사전에 최적화하고 있다는 신호일 수 있습니다. 하지만 이러한 작업이 반복적으로 표시되면 데이터가 본질적으로 왜곡되었거나 쿼리로 인해 과도한 데이터 셔플이 발생하고 있는 것일 수 있습니다.

최적화

REPARTITION 단계 수를 줄이려면 다음을 시도해 보세요.

  • 데이터 배포: 테이블이 효과적으로 파티션화되고 클러스터링되어 있는지 확인합니다. 데이터가 잘 분산되면 셔플 후 심각한 불균형이 발생할 가능성이 줄어듭니다.
  • 쿼리 구조: 데이터 왜곡의 잠재적 소스에 대한 쿼리를 분석합니다. 예를 들어 매우 선택적인 필터나 조인으로 인해 단일 작업자에서 처리되는 데이터 하위 집합이 적은가요?
  • 조인 전략: 다양한 조인 전략을 실험하여 더 균형 잡힌 데이터 분포를 가져오는지 확인합니다.

COALESCE 단계 수를 줄이려면 다음을 시도해 보세요.

  • 집계 전략: 쿼리 파이프라인에서 집계를 더 일찍 실행하는 것이 좋습니다. 이렇게 하면 COALESCE 단계를 일으킬 수 있는 작은 중간 결과 집합의 수를 줄일 수 있습니다.
  • 데이터 양: 매우 작은 데이터 세트를 처리하는 경우 COALESCE는 큰 문제가 아닐 수 있습니다.

과도하게 최적화하지 마세요. 너무 이른 시기에 최적화하면 큰 이득을 얻지 못하면서 쿼리가 더 복잡해질 수 있습니다.

통합 쿼리에 대한 설명

통합 쿼리를 사용하면 EXTERNAL_QUERY 함수를 통해 쿼리 문을 외부 데이터 소스에 보낼 수 있습니다. 통합 쿼리에는 SQL 푸시다운이라는 최적화 기법이 적용되며 쿼리 계획에는 외부 데이터 소스(있는 경우)로 푸시다운된 작업이 표시됩니다. 예를 들어 다음 쿼리를 실행하는 경우:

SELECT id, name
FROM EXTERNAL_QUERY("<connection>", "SELECT * FROM company")
WHERE country_code IN ('ee', 'hu') AND name like '%TV%'

쿼리 계획에 다음 스테이지 단계가 표시됩니다.

$1:id, $2:name, $3:country_code
FROM table_for_external_query_$_0(
  SELECT id, name, country_code
  FROM (
    /*native_query*/
    SELECT * FROM company
  )
  WHERE in(country_code, 'ee', 'hu')
)
WHERE and(in($3, 'ee', 'hu'), like($2, '%TV%'))
$1, $2
TO __stage00_output

이 계획에서 table_for_external_query_$_0(...)EXTERNAL_QUERY 함수를 나타냅니다. 괄호 안에는 외부 데이터 소스가 실행하는 쿼리가 표시됩니다. 이를 바탕으로 다음 사항을 확인할 수 있습니다.

  • 외부 데이터 소스는 선택된 3개의 열만 반환합니다.
  • 외부 데이터 소스는 country_code'ee' 또는 'hu'인 행만 반환합니다.
  • LIKE 연산자는 푸시다운되지 않으며 BigQuery에서 평가됩니다.

비교를 위해 푸시다운이 없는 경우에는 쿼리 계획에서 다음 스테이지 단계를 표시합니다.

$1:id, $2:name, $3:country_code
FROM table_for_external_query_$_0(
  SELECT id, name, description, country_code, primary_address, secondary address
  FROM (
    /*native_query*/
    SELECT * FROM company
  )
)
WHERE and(in($3, 'ee', 'hu'), like($2, '%TV%'))
$1, $2
TO __stage00_output

이번에는 외부 데이터 소스가 company 테이블의 모든 열과 모든 행을 반환하고 BigQuery에서 필터링을 수행합니다.

타임라인 메타데이터

쿼리 타임라인은 특정 시점에서 진행 상황을 보고하여 전체 쿼리 진행의 스냅샷 보기를 제공합니다. 타임라인은 다음 세부정보를 보고하는 일련의 샘플로 표시됩니다.

필드 설명
elapsedMs 쿼리 실행이 시작된 후 경과된 시간(밀리초 단위)
totalSlotMs 쿼리에서 사용된 슬롯-밀리초 누적 표시
pendingUnits 예약 작업 및 실행 대기 중인 작업의 총 단위
activeUnits 작업자가 처리 중인 총 활성 작업 단위
completedUnits 이 쿼리를 실행하는 동안 완료된 총 작업 단위

예시 쿼리

다음 쿼리는 셰익스피어 공개 데이터 세트에서 행 수를 계산하고 '햄릿'을 참조하는 행으로 결과를 제한하는 두 번째 조건부 개수를 포함합니다.

SELECT
  COUNT(1) as rowcount,
  COUNTIF(corpus = 'hamlet') as rowcount_hamlet
FROM `publicdata.samples.shakespeare`

쿼리 계획을 보려면 실행 세부정보를 클릭합니다.

햄릿 쿼리 계획입니다.

색상 표시기는 모든 스테이지에서 모든 단계에 대한 상대적 타이밍을 나타냅니다.

실행 스테이지의 단계에 대해 자세히 알아보려면 을 클릭하여 스테이지 세부정보를 확장합니다.

햄릿 쿼리 계획 단계 세부정보입니다.

이 예시에서 세그먼트에서 가장 긴 시간은 스테이지 01에 있는 단일 작업자가 스테이지 00이 완료되기를 기다리는 시간이었습니다. 그 이유는 스테이지 01이 스테이지 00 입력에 종속되어 있고 첫 번째 스테이지가 중간 셔플에 출력을 쓰기 전까지 시작될 수 없기 때문입니다.

오류 보고

쿼리 작업이 실행 중간에 실패할 수 있습니다. 계획 정보가 주기적으로 업데이트되므로, 실행 그래프에서 오류가 발생한 위치를 관찰할 수 있습니다. Google Cloud 콘솔 내에서 성공 또는 실패한 단계는 단계 이름 옆에 있는 체크표시 또는 느낌표로 표시됩니다.

오류 해석 및 해결에 대한 자세한 내용은 문제 해결 가이드를 참조하세요.

API 샘플 표현

쿼리 계획 정보는 작업 응답 정보에 포함되며, jobs.get을 호출하여 검색할 수 있습니다. 예를 들어 샘플 햄릿 쿼리를 반환하는 작업에 대한 JSON 응답의 다음 발췌물에는 쿼리 계획과 타임라인 정보가 모두 표시됩니다.

"statistics": {
  "creationTime": "1576544129234",
  "startTime": "1576544129348",
  "endTime": "1576544129681",
  "totalBytesProcessed": "2464625",
  "query": {
    "queryPlan": [
      {
        "name": "S00: Input",
        "id": "0",
        "startMs": "1576544129436",
        "endMs": "1576544129465",
        "waitRatioAvg": 0.04,
        "waitMsAvg": "1",
        "waitRatioMax": 0.04,
        "waitMsMax": "1",
        "readRatioAvg": 0.32,
        "readMsAvg": "8",
        "readRatioMax": 0.32,
        "readMsMax": "8",
        "computeRatioAvg": 1,
        "computeMsAvg": "25",
        "computeRatioMax": 1,
        "computeMsMax": "25",
        "writeRatioAvg": 0.08,
        "writeMsAvg": "2",
        "writeRatioMax": 0.08,
        "writeMsMax": "2",
        "shuffleOutputBytes": "18",
        "shuffleOutputBytesSpilled": "0",
        "recordsRead": "164656",
        "recordsWritten": "1",
        "parallelInputs": "1",
        "completedParallelInputs": "1",
        "status": "COMPLETE",
        "steps": [
          {
            "kind": "READ",
            "substeps": [
              "$1:corpus",
              "FROM publicdata.samples.shakespeare"
            ]
          },
          {
            "kind": "AGGREGATE",
            "substeps": [
              "$20 := COUNT($30)",
              "$21 := COUNTIF($31)"
            ]
          },
          {
            "kind": "COMPUTE",
            "substeps": [
              "$30 := 1",
              "$31 := equal($1, 'hamlet')"
            ]
          },
          {
            "kind": "WRITE",
            "substeps": [
              "$20, $21",
              "TO __stage00_output"
            ]
          }
        ]
      },
      {
        "name": "S01: Output",
        "id": "1",
        "startMs": "1576544129465",
        "endMs": "1576544129480",
        "inputStages": [
          "0"
        ],
        "waitRatioAvg": 0.44,
        "waitMsAvg": "11",
        "waitRatioMax": 0.44,
        "waitMsMax": "11",
        "readRatioAvg": 0,
        "readMsAvg": "0",
        "readRatioMax": 0,
        "readMsMax": "0",
        "computeRatioAvg": 0.2,
        "computeMsAvg": "5",
        "computeRatioMax": 0.2,
        "computeMsMax": "5",
        "writeRatioAvg": 0.16,
        "writeMsAvg": "4",
        "writeRatioMax": 0.16,
        "writeMsMax": "4",
        "shuffleOutputBytes": "17",
        "shuffleOutputBytesSpilled": "0",
        "recordsRead": "1",
        "recordsWritten": "1",
        "parallelInputs": "1",
        "completedParallelInputs": "1",
        "status": "COMPLETE",
        "steps": [
          {
            "kind": "READ",
            "substeps": [
              "$20, $21",
              "FROM __stage00_output"
            ]
          },
          {
            "kind": "AGGREGATE",
            "substeps": [
              "$10 := SUM_OF_COUNTS($20)",
              "$11 := SUM_OF_COUNTS($21)"
            ]
          },
          {
            "kind": "WRITE",
            "substeps": [
              "$10, $11",
              "TO __stage01_output"
            ]
          }
        ]
      }
    ],
    "estimatedBytesProcessed": "2464625",
    "timeline": [
      {
        "elapsedMs": "304",
        "totalSlotMs": "50",
        "pendingUnits": "0",
        "completedUnits": "2"
      }
    ],
    "totalPartitionsProcessed": "0",
    "totalBytesProcessed": "2464625",
    "totalBytesBilled": "10485760",
    "billingTier": 1,
    "totalSlotMs": "50",
    "cacheHit": false,
    "referencedTables": [
      {
        "projectId": "publicdata",
        "datasetId": "samples",
        "tableId": "shakespeare"
      }
    ],
    "statementType": "SELECT"
  },
  "totalSlotMs": "50"
},

실행 정보 사용

BigQuery 쿼리 계획은 서비스의 쿼리 실행 방법에 대한 정보를 제공하지만, 서비스의 관리 특성에 따라 일부 세부정보의 직접적인 조치 가능 여부가 제한됩니다. 서비스를 사용하면 많은 최적화가 자동으로 수행되며, 이러한 최적화는 미세 조정, 프로비저닝 및 모니터링을 위해 전문 지식을 갖춘 전담 직원이 필요할 수 있는 다른 환경과 다를 수 있습니다.

쿼리 실행과 성능을 향상시킬 수 있는 특정 기술에 대해서는 권장사항 문서를 참조하세요. 쿼리 계획과 타임라인 통계는 어떤 스테이지에 리소스 사용이 집중되는지 이해하는 데 유용할 수 있습니다. 예를 들어 입력 행보다 훨씬 많은 출력 행을 생성하는 JOIN 스테이지는 쿼리에서 일찍 필터링하는 것이 좋습니다.

또한 타임라인 정보는 지정된 쿼리가 독립적인 상태에서 느린 것인지 또는 같은 리소스를 놓고 경합하는 다른 쿼리의 영향으로 인해 느린 것인지 식별하는 데 유용할 수 있습니다. 쿼리 전체 기간 중에 활성 단위 수가 제한된 상태로 유지되지만 큐에 추가된 작업 단위 양이 높게 유지되는 것으로 관측될 경우 동시 쿼리 수를 줄이면 특정 쿼리의 전체 실행 시간을 크게 향상시킬 수 있습니다.