クエリプランとタイムライン

BigQuery のクエリジョブには、診断で使用できるクエリプランとタイミング情報が埋め込まれます。これは、他のデータベースや分析システムの EXPLAIN ステートメントなどで提供される情報に似ています。この情報は、jobs.get などのメソッドで API レスポンスから取得できます。

長期実行されるクエリの場合、BigQuery はこの統計情報を定期的に更新します。この更新は、ジョブ ステータスのポーリングと関係なく実行されますが、通常、更新間隔が 30 秒よりも短くなることはありません。また、実行リソースを使用しないクエリジョブ(ドライラン リクエストや、キャッシュから結果が提供される場合など)の場合、診断情報はありませんが、他の統計情報が提供される可能性があります。

背景

BigQuery がクエリジョブを実行すると、宣言型の SQL 文を実行グラフに変換し、一連のクエリステージに分割します。クエリステージは、より細かい実行ステップから構成されます。BigQuery は、高度に分散された並列アーキテクチャを利用して、これらのクエリを実行します。ステージは、多くのワーカーを同時に実行できる作業単位をモデル化しています。ステージは、高速の分散シャッフル アーキテクチャを介して相互に通信を行います。このアーキテクチャの詳細については、こちらをご覧ください。

クエリプランでは、並列処理に関する情報を扱うため、作業単位とワーカーという用語を使用しています。BigQuery では、コンピューティング、メモリ、I/O リソースなど、クエリの実行に必要な複数のファセットを抽象的に表すために「スロット」という用語を使用する場合があります。ジョブ統計の概要では、この抽象的な単位に基づき、個々のクエリの totalSlotMs を表示しています。

クエリ実行のアーキテクチャでもう 1 つの重要な特性は動的である点です。つまり、クエリの実行中にクエリプランが変更される可能性があります。クエリの実行中に追加されるステージは、主にクエリワーカー全体でデータ処理を向上させるために使用されます。通常、このようなクエリプランにはパーティション再設定ステージというラベルが付きます。

クエリプランに加えて、クエリジョブは実行のタイムラインを公開します。これにより、クエリワーカー内で完了している作業単位、保留中の作業単位、アクティブな作業単位の数を確認できます。1 つのクエリで複数のステージが同時に処理される場合もあるため、タイムラインはクエリ全体の進行状況を把握する際に役立ちます。

BigQuery ウェブ UI で情報を表示する

BigQuery ウェブ UI で [Results] ボタンの右側にある [Details] ボタンをクリックすると、完了したクエリのクエリプランの詳細が表示されます。

クエリプランのスクリーンショット

長時間実行されているクエリの場合、クエリ コンポーザ ペインの下にあるクエリ ステータスのリンクをクリックすると、クエリプランの進行状況を確認できます。

経過時間のステータス行が表示されたスクリーンショット

クエリプランの情報

API レスポンスで、クエリプランはクエリステージのリストとして表され、ステージごとの概要、詳細なステップ情報、ステージ タイミングの分類が公開されます。API レスポンスに含まれていても、ウェブ UI に表示されない情報もあります。

ステージの概要

各ステージには、次のような概要フィールドがあります。

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 入力テーブルまたは中間シャッフルからの 1 つ以上の列の読み取り。
WRITE 出力テーブルまたは中間結果への 1 つ以上の列の書き込み。ステージからの HASH パーティショニングされた出力の場合は、パーティション キーとして使用される列も含まれます。
COMPUTE 式の評価や SQL 関数などのオペレーション。
FILTER WHERE 句、OMIT IF 句、HAVING 句を実装する演算子。
SORT 並べ替えオペレーション。列のキーと並べ替えの方向が含まれます。
AGGREGATE GROUP BY または COUNT などの集計オペレーション。
LIMIT LIMIT 句を実装する演算子。
JOIN JOIN オペレーション。結合の種類や、使用される列が含まれます。
ANALYTIC_FUNCTION 分析関数の呼び出し(「ウィンドウ関数」とも呼ばれます)。
USER_DEFINED_FUNCTION ユーザー定義関数の呼び出し。

ステージごとのタイミング情報

クエリステージは、相対的または絶対的な形式でステージのタイミング情報を提供します。実行の各ステージは、1 つ以上の独立したワーカーによって実行された作業を表すため、情報は平均時間と最長時間の両方で提供されます。これは、ステージ内のすべてのワーカーの平均的なパフォーマンスと、長時間ワーカーのパフォーマンスを表します。平均時間と最長時間は、絶対的な形式と相対的な形式で表現されます。比率ベースの統計の場合、セグメントのワーカーによって費やされた最長時間との比率でデータが提供されます。

ウェブ UI では、相対的な表現でステージのタイミングが表示されます。

ステージのタイミング情報は次のように表示されます。

相対的なタイミング 絶対的なタイミング ウェブ UI*
waitRatioAvg waitMsAvg waitRatioAvg の暗黄色のバー
waitRatioMax waitMsMax waitRatioMax の黄色のバー
readRatioAvg readMsAvg readRatioAvg の暗紫色のバー
readRatioMax readMsMax readRatioMax の紫色のバー
computeRatioAvg computeMsAvg computeRatioAvg の暗いオレンジ色のバー
computeRatioMax computeMsMax computeRatioMax のオレンジ色のバー
writeRatioAvg writeMsAvg writeRatioAvg の暗青色のバー
writeRatioMax writeMsMax writeRatioMax の青色のバー

* ラベル "AVG" と "MAX" は図だけのものであり、実際のウェブ UI には表示されません。

タイムライン メタデータ

クエリ タイムラインは、特定の時点での進捗状況を表し、クエリ全体の進捗状況を示すスナップショットとなります。タイムラインは一連のサンプルとして表され、以下の情報を提供します。

項目 説明
elapsedMs クエリを開始してからのから経過時間(ミリ秒)。
totalSlotMs クエリで使用されるスロットの合計処理時間(ミリ秒)。
pendingUnits スケジュールされ、実行待ちの作業単位の合計数。
activeUnits ワーカーが現在処理している作業単位の合計数。
completedUnits このクエリの実行中に完了した作業単位の合計数。

クエリの例

Shakespeare 一般公開データセットの行数を計算し、結果から Hamlet に関連する行数を絞り込む簡単なクエリを実行します。

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

[Details] をクリックすると、クエリプランに関する次の情報が表示されます。まず、クエリのタイムラインを含む最初のセクションを見てみましょう。

Hamlet クエリのタイムラインが表示されたスクリーンショット

この例では、非常に小さなサンプル テーブルと単純なクエリを扱っているため、合計で 2 つの作業単位しかありません。この例では、すべての作業がすぐに完了します。

クエリプランを詳しく見てみましょう。

Hamlet クエリプランのスクリーンショット

このカラー インジケーターはステージの相対的なタイミングを表しています。同時入力の情報を見ると、各ステージで 1 つのワーカーしか必要としないため、平均時間と最長時間のタイミングに差はありません。

このクエリでは、ステージ 01 のワーカーがステージ 00 の完了を待っていた時間がセグメントの最長時間になっています。これは、ステージ 01 がステージ 00 の入力に依存しており、最初のステージがその出力(1 行、18 バイトまで)を中間シャッフルに書き込むまで開始できないためです。

実行ステージのステップをさらに詳しく見てみましょう。ステージラベルの左側にある三角形をクリックして、ステージの詳細を展開します。

Hamlet クエリプランの詳細が表示されたスクリーンショット

この例では、ステージ 00 の作業を完了した単一ワーカーの実行プランを確認できます。まず、参照先の Shakespeare テーブルの corpus 列からデータを読み込んでいます。次に、COUNTCOUNTIF を集計しています。データのスキャンで COMPUTE ステップを実行し、通常の件数と条件付きの件数を計算して、出力を中間シャッフルに書き込んでいます。このプランでは、出力に __stage00_output というラベルが付いています。

エラー報告

実行中にクエリジョブが失敗することもあります。プラン情報は定期的に更新されるため、実行グラフ内で失敗した場所を確認できます。UI では、ステージ名の横にチェックマークと感嘆符が付き、ステージの成功と失敗を表しています。

エラーの意味と対処方法については、トラブルシューティング ガイドをご覧ください。

API サンプルの表現

クエリプラン情報は、追加のメソッドを呼び出すことなく、ジョブのレスポンス情報に自動的に埋め込まれます。ジョブの詳細を取得するには、jobs.get を呼び出します。以下は、Hamlet クエリの結果を返すジョブの JSON レスポンスの一部で、クエリプランとタイムライン情報が表示されています。

"statistics": {
  "query": {
    "cacheHit": false,
    "queryPlan": [
      {
        "completedParallelInputs": "1",
        "computeMsAvg": "25",
        "computeMsMax": "25",
        "computeRatioAvg": 0.17857142857142858,
        "computeRatioMax": 0.17857142857142858,
        "endMs": "1522787349945",
        "id": "0",
        "name": "S00: Input",
        "parallelInputs": "1",
        "readMsAvg": "28",
        "readMsMax": "28",
        "readRatioAvg": 0.2,
        "readRatioMax": 0.2,
        "recordsRead": "164656",
        "recordsWritten": "1",
        "shuffleOutputBytes": "18",
        "shuffleOutputBytesSpilled": "0",
        "startMs": "1522787349898",
        "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"
            ]
          }
        ],
        "waitMsAvg": "0",
        "waitMsMax": "0",
        "waitRatioAvg": 0.0,
        "waitRatioMax": 0.0,
        "writeMsAvg": "5",
        "writeMsMax": "5",
        "writeRatioAvg": 0.03571428571428571,
        "writeRatioMax": 0.03571428571428571
      },
      {
        "completedParallelInputs": "1",
        "computeMsAvg": "14",
        "computeMsMax": "14",
        "computeRatioAvg": 0.1,
        "computeRatioMax": 0.1,
        "endMs": "1522787350180",
        "id": "1",
        "inputStages": [
          "0"
        ],
        "name": "S01: Output",
        "parallelInputs": "1",
        "readMsAvg": "0",
        "readMsMax": "0",
        "readRatioAvg": 0.0,
        "readRatioMax": 0.0,
        "recordsRead": "1",
        "recordsWritten": "1",
        "shuffleOutputBytes": "16",
        "shuffleOutputBytesSpilled": "0",
        "startMs": "1522787350038",
        "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"
            ]
          }
        ],
        "waitMsAvg": "140",
        "waitMsMax": "140",
        "waitRatioAvg": 1.0,
        "waitRatioMax": 1.0,
        "writeMsAvg": "129",
        "writeMsMax": "129",
        "writeRatioAvg": 0.9214285714285714,
        "writeRatioMax": 0.9214285714285714
      }
    ],
    "referencedTables": [
      {
        "datasetId": "samples",
        "projectId": "publicdata",
        "tableId": "shakespeare"
      }
    ],
    "statementType": "SELECT",
    "timeline": [
      {
        "activeUnits": "0",
        "completedUnits": "2",
        "elapsedMs": "999",
        "pendingUnits": "0",
        "totalSlotMs": "185"
      },
      {
        "activeUnits": "0",
        "completedUnits": "2",
        "elapsedMs": "1197",
        "pendingUnits": "0",
        "totalSlotMs": "185"
      }
    ],
    "totalBytesBilled": "10485760",
    "totalBytesProcessed": "2464625",
    "totalPartitionsProcessed": "0",
    "totalSlotMs": "127"
  },
  "totalBytesProcessed": "2464625"
},

実行情報を使用する

BigQuery クエリプランでは、サービスによるクエリの実行方法に関する情報が提供されますが、サービスの管理特性のため、この情報をそのまま利用できるとは限りません。多くの最適化はサービスによって自動的に実行されますが、チューニング、プロビジョニング、モニタリングで専門知識のある専任スタッフが必要になる場合もあり、どの環境でも同じサービスが使用されるとは限りません。

クエリの実行とパフォーマンスを向上させる具体的な手段については、ベスト プラクティスのドキュメントをご覧ください。クエリプランとタイムラインの統計情報は、特定のステージでのリソースの使用状況の把握に役立ちます。たとえば、JOIN ステージで入力行よりも出力行が多い場合は、クエリの前にフィルタリングが必要かもしれません。

また、タイムライン情報を見ると、特定のクエリだけが遅いのか、同じリソースを利用する別のクエリとの競合で遅いのかを識別できます。クエリの全期間を通じでアクティブな作業単位の数が限定されていても、キューに入る作業単位の数が多い場合は、同時クエリの数を減らすことで、特定のクエリの全体的な実行時間を短縮できる可能性があります。

このページは役立ちましたか?評価をお願いいたします。

フィードバックを送信...