쿼리 계획 및 타임라인

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

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

배경

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

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

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

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

Cloud Console을 사용하여 정보 보기

Cloud Console에서 실행 세부정보 버튼(결과 버튼 옆)을 클릭하면 완료된 쿼리에 대한 쿼리 계획의 세부정보를 볼 수 있습니다.

쿼리 계획입니다.

쿼리 계획 정보

API 응답 내에서 쿼리 계획은 쿼리 스테이지 목록으로 표시됩니다. 목록의 각 항목은 스테이지별 개요 통계, 세부 단계 정보, 스테이지 타이밍 분류를 보여줍니다. Cloud Console 내부에서 모든 세부정보가 렌더링되지는 않지만 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 스테이지 간에 중요한 데이터를 전송하는 쿼리를 디스크 기반 전송으로 대체해야 할 수도 있습니다. 분산된 바이트 통계는 디스크로 분산된 데이터의 양을 알려줍니다. 이는 최적화 알고리즘에 따라 다르므로 특정 쿼리에 대해 확정적이지 않습니다.

스테이지별 단계 정보

단계는 스테이지 내에서 각 작업자가 실행해야 하는 더욱 세분화된 작업을 나타내며 정렬된 작업 목록으로 제공됩니다. 단계는 카테고리화되며 일부 작업은 보다 세부적인 정보를 제공합니다. 쿼리 계획의 작업 카테고리는 다음과 같습니다.

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

스테이지별 타이밍 분류

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

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

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

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

타임라인 메타데이터

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

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

예시 쿼리

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

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

이 예시에서는 매우 작은 샘플 테이블과 간단한 쿼리를 사용하기 때문에 총 작업 단위가 2개만 있습니다. 모든 작업은 거의 즉시 완료됩니다.

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

햄릿 쿼리 계획입니다.

이 예시에서 색상 표시기는 모든 스테이지의 상대적 타이밍을 나타냅니다. 병렬 입력 정보를 보면 각 스테이지에 단일 작업자만 필요하다는 것을 알 수 있습니다. 따라서 평균 타이밍과 가장 느린 타이밍 사이에 변동이 없습니다.

또한 이 간단한 쿼리의 경우, 세그먼트에서 가장 긴 시간은 스테이지 01에 있는 단일 작업자가 스테이지 00이 완료되기를 기다리는 시간이었습니다. 그 이유는 스테이지 01이 스테이지 00 입력에 종속되어 있고 첫 번째 스테이지가 중간 셔플에 출력(1행, 약 18바이트)을 쓰기 전까지 시작될 수 없기 때문입니다.

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

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

스테이지 00의 작업을 완료한 단일 작업자의 실행 계획은 먼저 참조된 셰익스피어 테이블의 'corpus' 열에서 데이터가 읽혀졌음을 보여줍니다. 그 다음 COUNTCOUNTIF 프로젝션에 대해 AGGREGATIONS가 설정되었습니다. 데이터를 스캔하려면 COMPUTE 단계가 필요했는데, 이 단계에서는 정상 및 조건 개수에 대한 데이터가 제공되었으며 출력은 이 계획에서 __stage00_output 라벨이 지정된 중간 셔플 출력에 기록되었습니다.

오류 보고

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

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

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 스테이지는 쿼리에서 일찍 필터링하는 것이 좋습니다.

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