スロットの自動スケーリングの概要

スロットの自動スケーリングを使用するように構成した予約では、ワークロードの需要に合わせて、割り当てられる容量が自動的にスケーリングされます。ワークロードの増減に合わせて、BigQuery はスロットを適切なレベルに動的に調整します。スロットの自動スケーリングを使用した予約は、BigQuery エディションでのみ使用できます。

自動スケーリングの予約を使用する

自動スケーリングの予約を作成する前に、スロット コミットメントを購入する必要はありません。スロット コミットメントでは、一貫して使用されるスロットに対して割引料金が適用されますが、自動スケーリングの予約ではこれはオプションです。自動スケーリングの予約を作成するには、予約に最大数のスロット(最大予約サイズ)を割り当てます。自動スケーリング スロットの最大数は、最大予約サイズから、予約に割り当てられたオプションのベースライン スロットをすべて差し引くことで特定できます。

自動スケーリングの予約を作成する場合は、次の点を考慮してください。

  • BigQuery は、ジョブの実行に必要なスロット数に達するか、予約に使用できるスロットの最大数に達するまで、予約をほぼ瞬時にスケーリングします。スロットは常に 50 の倍数に自動スケーリングされます。
  • スケールアップは実際の使用量に基づいており、50 スロット単位で切り上げられます。
  • 自動スケーリングされたスロットには、スケールアップの際に、関連するエディションの容量コンピューティング料金が適用されます。請求は、使用したスロットの数ではなく、スケーリングされたスロットの数に対して行われます。この料金は、BigQuery をスケールアップするジョブが失敗した場合にも適用されます。このため、請求と照合するためにジョブ情報スキーマを使用しないでください。代わりに、情報スキーマを使用して自動スケーリングをモニタリングするをご覧ください。
  • スロット数は常に 50 の倍数でスケーリングされますが、1 ステップで 50 を超えるスロットがスケーリングされる場合があります。たとえば、ワークロードで 450 スロットを追加する必要がある場合、BigQuery は容量要件を満たすために一度に 450 スロットをスケーリングしようとする可能性があります。
  • 予約に関連付けられたジョブが容量を必要としなくなった場合、BigQuery はスケールダウンします(最小 1 分)。

自動スケーリングされた容量は 60 秒以上保持されます。この 60 秒をスケールダウン ウィンドウと呼びます。容量のピークが発生すると、スケールダウン ウィンドウがリセットされ、容量レベル全体が新しい割り当てとして扱われます。ただし、前回の容量増加から 60 秒以上が経過し、需要が減少している場合、システムはスケールダウン ウィンドウをリセットせずに容量を削減します。これにより、遅延なく連続して削減が行われます。

自動スケーリングの操作方法については、スロットの自動スケーリングの操作をご覧ください。

ベースライン スロットと自動スケーリング スロットとともに予約を使用する

最大予約サイズを指定するだけでなく、必要に応じて予約あたりのスロットのベースライン数を指定できます。ベースラインは予約に常に割り当てられる最小スロット数であり、常にそれらに対して課金されます。自動スケーリング スロットは、すべてのベースライン スロット(と、該当する場合はアイドル スロット)が使用された後にのみ追加されます。1 つの予約のアイドル ベースライン スロットは、容量を必要とする他の予約と共有できます。

予約のベースライン スロット数は数分ごとに増やすことができます。ベースライン スロットを減らすのは、ベースライン スロット容量を最近変更し、ベースライン スロットがコミット済みスロットを超えた場合に、1 時間に 1 回に制限されます。それ以外は、ベースライン スロットを数分ごとに減らすことができます。

ベースライン スロットと自動スケーリング スロットは、最近のワークロードに基づいて容量を提供することを目的としています。最近のワークロードとかなり異なる大規模のワークロードが予想される場合は、ワークロードの容量に対応する自動スケーリング スロットをあてにするのではなく、イベントの前にベースライン容量を増やすことをおすすめします。ベースライン容量の増加に問題が発生した場合は、15 分待ってからリクエストを再試行してください。

予約にベースライン スロットがない場合、または他の予約からアイドル スロットを借用するように構成されていない場合、BigQuery はスケーリングを試みます。 それ以外は、スケーリングの前にベースライン スロットを十分に活用する必要があります。

予約では、次の優先度でスロットを使用および追加します。

  1. ベースライン スロット。
  2. アイドル スロットの共有(有効になっている場合)。予約では、同じエディションと同じリージョンで作成された他の予約のアイドル ベースライン スロットまたはコミット済みスロットのみを共有できます。
  3. 自動スケーリング スロット。

次の例では、スロットは指定されたベースラインの量からスケーリングされます。etl 予約と dashboard 予約は、それぞれベースライン サイズが 700 スロットと 300 スロットです。

コミットメントのない自動スケーリングの例。

この例では、etl 予約を 1,300 スロットにスケーリングできます(700 ベースライン スロット足す 600 自動スケーリング スロット)。dashboard 予約が使用されていない場合、etl 予約は、そこでジョブが何も実行されていなければ、dashboard 予約から 300 スロットを使用できるため、使用できるスロット数は最大 1,600 になります。

dashboard 予約は、1,100 スロット(300 ベースライン スロット足す 800 自動スケーリング スロット)にスケーリングできます。etl 予約が完全にアイドル状態の場合、dashboard 予約では、最大 1,800 スロット(etl 予約で 300 ベースライン スロット足す 800 自動スケーリング スロット足す 700 アイドル スロット)にスケーリングできます。

etl 予約で常に使用可能な 700 を超えるベースライン スロットが必要な場合、次の順序でスロットの追加が試されます。

  1. 700 ベースライン スロット。
  2. dashboard 予約の 300 ベースライン スロットとの共有のアイドル スロット。予約は、同じエディションで作成された他の予約とアイドル状態のベースライン スロットを共有することだけできます。
  3. 最大予約サイズまで 600 の追加スロットをスケールアップします。

スロット コミットメントの使用

次の例は、容量コミットメントを使用したスロットの自動スケーリングを示しています。

自動スケーリングの例

予約ベースラインと同様に、スロット コミットメントでは、すべての予約で使用可能な固定数のスロットを割り当てることを許可します。ベースライン スロットとは異なり、期間中はコミットメントを削減できません。スロット コミットメントはオプションですが、ベースライン スロットが長期間必要な場合は、費用を節約できます。

この例では、容量コミットメント スロットに対して事前定義されたレートで課金されます。自動スケーリングが有効になり、予約がアップスケールされた状態になると、自動スケーリング スロットの数に対してスケーリング料金が課金されます。 自動スケーリング料金では、使用されたスロット数ではなく、スケーリングされたスロット数に対して課金されます。

使用可能なスロットの最大数

予約で使用できるスロットの最大数は、ベースライン スロット、自動スケーリング スロットの最大数、および同じエディションで作成されて、ベースライン スロットで対応されていないあらゆるスロットを追加することで、計算できます。前の画像の例では、次のように設定しています。

  • 年間 1,000 スロットの容量コミットメント。それらのスロットは、etl 予約と dashboard 予約でベースライン スロットとして割り当てられます。
  • etl 予約に割り当てられる 700 のベースライン スロット。
  • dashboard 予約に割り当てられる 300 のベースライン スロット。
  • etl 予約用の 600 個の自動スケーリング スロット。
  • dashboard 予約用の 800 個の自動スケーリング スロット。

etl 予約の場合、使用可能なスロットの最大数は etl ベースライン スロット(700)足す、dashboard ベースライン スロット(すべてのスロットがアイドル状態の場合、300)足す、自動スケーリング スロットの最大数(600)に等しいです。したがって、この例で etl 予約に使用できるスロットの最大数は 1,600 です。この数は容量コミットメントの数を超えています。

次の例では、年間のコミットメントが割り当てられたベースライン スロット数を上回っています。

使用可能なスロットの計算

この例では、以下のものを得ます。

  • 年間 1,600 スロットの容量コミットメント。
  • 1,500 個(500 個の自動スケーリング スロットを含む)の最大予約サイズ。
  • etl 予約に割り当てられる 1,000 個のベースライン スロット。

予約に使用できるスロットの最大数は、ベースライン スロット(1,000)足す、ベースライン スロット専用ではないコミットされたアイドル スロット(1,600 個の年間スロット - 1,000 個のベースライン スロット = 600)足す、自動スケーリング スロットの数(500)に等しいです。したがって、この予約のスロットの取りうる最大数は 2,100 です。自動スケーリングされるスロットは、容量コミットメントを超える追加スロットです。

自動スケーリングのベスト プラクティス

  1. オートスケーラーを初めて使用するときは、自動スケーリング スロットの数を、過去および予想されるパフォーマンスに基づいて意味のある数に設定します。予約を作成したら、失敗率、パフォーマンス、請求をアクティブにモニタリングして、必要に応じて自動スケーリング スロットの数を調整します。

  2. オートスケーラーのスケールダウンには最低でも 1 分はかかるため、パフォーマンスと費用のバランスを考慮して自動スケーリング スロットの最大数を設定する必要があります。自動スケーリング スロットの最大数が大きく、ジョブがすべてのスロットを使用して数秒で完了する場合でも、1 分間の最大スロット数分の費用が発生します。最大スロットを現在の半分に減らすと、予約はより少ない数にスケーリングされ、その分ジョブは 1 分間に多くの slot_seconds を使用できるため、無駄が減ります。スロットの要件を判断する方法については、ジョブのパフォーマンスをモニタリングするをご覧ください。スロット要件を決定する別の方法については、エディション スロットの推奨事項を表示するをご覧ください。

  3. スロットの使用量は、ベースラインとスケーリングされたスロットの合計を超える場合があります。ベースラインとスケーリングされたスロットの合計を超えるスロットの使用量に対しては課金されません。

  4. オートスケーラーは、複数の同時実行クエリを持つワークロードなど、大規模で時間がかかるワークロードに最適です。クエリは 1 個ずつ送信しないでください。クエリごとに予約がスケーリングされ、スケーリングは最低でも 1 分間維持されます。クエリを継続的に送信し、ワークロードが一定の場合、ベースラインを設定して確約購入すると、割引価格で一定の容量が提供されます。

  5. BigQuery の自動スケーリングは、容量の可用性の影響を受けます。BigQuery は、過去の使用状況に基づいてお客様の容量需要を満たそうとします。容量を保証するために、オプションのスロット ベースライン(予約で保証されるスロット数)を設定できます。ベースラインを使用すると、スロットは直ちに利用可能になり、使用するかどうかにかかわらず課金されます。大規模な需要(祝日におけるトラフィックの急増など)のために容量を確保するには、数週間前に BigQuery チームにお問い合わせください。

  6. ベースライン スロットに対しては常に課金されます。容量コミットメントが期限切れになった場合は、不要な料金が発生しないように、予約のベースライン スロット数を手動で調整する必要があります。たとえば、100 スロットの 1 年間のコミットメントと 100 ベースライン スロットの予約があるとします。コミットメントが期限切れになり、更新プランはありません。コミットメントが期限切れになると、従量課金制で 100 ベースライン スロットに対して支払いを行うことになります。

自動スケーリングのモニタリング

管理リソースグラフでスロット使用量をモニタリングすると、グラフでアライメント期間の使用スロット数が平準化されるため、スロット使用量よりも大幅にスケーリングされたスロットが表示されることがあります。自動スケーリングのスロット使用状況をより詳しく確認するには、期間オプションを短くします。これにより、アライメント期間が短い間隔に自動的に更新されます。

次の例のグラフでは、ワークロードの需要よりもはるかに多いスケーリング済みスロットが表示されています。

アライメント期間は 1 分間隔に設定されており、スロット使用量の需要よりも多くのスケーリング済みスロットが表示されています。

期間オプションを短くしてアライメント期間が 2 秒になるようにすると、オートスケーラーがワークロードの需要に合わせてスケーリングしていることを確認でき、より正確なデータが表示されます。期間オプションの開始範囲と終了範囲をドラッグして、期間オプションを調整できます。最も正確なワークロードの需要データを表示するには、[指標] リストから [p99] を選択します。

アライメント期間が 2 秒間隔に設定されており、スケーリング済みスロットがワークロードの需要に適したものになっています。

自動スケーリングの使用状況を最も正確に把握するには、1~15 秒のアライメント期間を使用します。管理リソースグラフのアライメント期間の詳細については、期間オプションをご覧ください。

スロット使用状況の確認については、管理リソースグラフを表示するをご覧ください。

情報スキーマを使用して自動スケーリングをモニタリングする

次の SQL スクリプトを使用すると、特定のエディションの請求対象スロット秒数を確認できます。これらのスクリプトは、予約が作成されたプロジェクトで実行する必要があります。最初のスクリプトでは、commitment_plan の対象となる請求対象スロット秒数が表示されます。2 つ目のスクリプトでは、コミットメントの対象とならない請求対象スロット秒数が表示されます。

これらのスクリプトは、次の 3 つの変数の値を設定するだけで実行できます。

  • start_time
  • end_time
  • edition_to_check

これらのスクリプトには、次の注意点があります。

  • 削除された予約と容量コミットメントは、データ保持期間の終了時に情報スキーマビューから削除されます。正しい結果を得るには、削除された予約とコミットメントを含まない最近の期間を指定します。

  • 四捨五入による誤差により、スクリプトの結果が請求額と完全に一致しない場合があります。

次のスクリプトは、特定のエディションのコミットメントでカバーされているスロットの使用状況を確認します。

開いて対象シナリオのスクリプトを表示する

DECLARE start_time,end_time TIMESTAMP;

DECLARE
  edition_to_check STRING;

/* Google uses Pacific Time to calculate the billing period for all customers,
regardless of their time zone. Use the following format if you want to match the
billing report. Change the start_time and end_time values to match the desired
window. */

/* The following three variables (start_time, end_time, and edition_to_check)
are the only variables that you need to set in the script.

During daylight savings time, the start_time and end_time variables should
follow this format: 2024-02-20 00:00:00-08. */

SET start_time = "2023-07-20 00:00:00-07";
SET end_time = "2023-07-28 00:00:00-07";
SET edition_to_check = 'ENTERPRISE';

/* The following function returns the slot seconds for the time window between
two capacity changes. For example, if there are 100 slots between (2023-06-01
10:00:00, 2023-06-01 11:00:00), then during that window the total slot seconds
will be 100 * 3600.

This script calculates a specific window (based on the variables defined above),
which is why the following script includes script_start_timestamp_unix_millis
and script_end_timestamp_unix_millis. */

CREATE TEMP FUNCTION
GetSlotSecondsBetweenChanges(
  slots FLOAT64,
  range_begin_timestamp_unix_millis FLOAT64,
  range_end_timestamp_unix_millis FLOAT64,
  script_start_timestamp_unix_millis FLOAT64,
  script_end_timestamp_unix_millis FLOAT64)
RETURNS INT64
LANGUAGE js
AS r"""
    if (script_end_timestamp_unix_millis < range_begin_timestamp_unix_millis || script_start_timestamp_unix_millis > range_end_timestamp_unix_millis) {
      return 0;
    }
    var begin = Math.max(script_start_timestamp_unix_millis, range_begin_timestamp_unix_millis)
    var end = Math.min(script_end_timestamp_unix_millis, range_end_timestamp_unix_millis)
    return slots * Math.ceil((end - begin) / 1000.0)
""";

/*
Sample CAPACITY_COMMITMENT_CHANGES data (unrelated columns ignored):
+---------------------+------------------------+-----------------+--------+------------+--------+
|  change_timestamp   | capacity_commitment_id | commitment_plan | state  | slot_count | action |
+---------------------+------------------------+-----------------+--------+------------+--------+
| 2023-07-20 19:30:27 | 12954109101902401697   | ANNUAL          | ACTIVE |        100 | CREATE |
| 2023-07-27 22:29:21 | 11445583810276646822   | FLEX            | ACTIVE |        100 | CREATE |
| 2023-07-27 23:10:06 | 7341455530498381779    | MONTHLY         | ACTIVE |        100 | CREATE |
| 2023-07-27 23:11:06 | 7341455530498381779    | FLEX            | ACTIVE |        100 | UPDATE |

The last row indicates a special change from MONTHLY to FLEX, which happens
because of commercial migration.

*/

WITH
  /*
  Information containing which commitment might have plan
  updated (e.g. renewal or commercial migration). For example:
  +------------------------+------------------+--------------------+--------+------------+--------+-----------+----------------------------+
  |  change_timestamp   | capacity_commitment_id | commitment_plan | state  | slot_count | action | next_plan | next_plan_change_timestamp |
  +---------------------+------------------------+-----------------+--------+------------+--------+-----------+----------------------------+
  | 2023-07-20 19:30:27 | 12954109101902401697   | ANNUAL          | ACTIVE |        100 | CREATE | ANNUAL    |        2023-07-20 19:30:27 |
  | 2023-07-27 22:29:21 | 11445583810276646822   | FLEX            | ACTIVE |        100 | CREATE | FLEX      |        2023-07-27 22:29:21 |
  | 2023-07-27 23:10:06 | 7341455530498381779    | MONTHLY         | ACTIVE |        100 | CREATE | FLEX      |        2023-07-27 23:11:06 |
  | 2023-07-27 23:11:06 | 7341455530498381779    | FLEX            | ACTIVE |        100 | UPDATE | FLEX      |        2023-07-27 23:11:06 |
  */
  commitments_with_next_plan AS (
    SELECT
      *,
      IFNULL(
        LEAD(commitment_plan)
          OVER (
            PARTITION BY capacity_commitment_id ORDER BY change_timestamp ASC
          ),
        commitment_plan)
        next_plan,
      IFNULL(
        LEAD(change_timestamp)
          OVER (
            PARTITION BY capacity_commitment_id ORDER BY change_timestamp ASC
          ),
        change_timestamp)
        next_plan_change_timestamp
    FROM
      `region-us.INFORMATION_SCHEMA.CAPACITY_COMMITMENT_CHANGES_BY_PROJECT`
  ),

  /*
  Insert a 'DELETE' action for those with updated plans. The FLEX commitment
  '7341455530498381779' is has no 'CREATE' action, and is instead labeled as an
  'UPDATE' action.

  For example:
  +---------------------+------------------------+-----------------+--------+------------+--------+
  |  change_timestamp   | capacity_commitment_id | commitment_plan | state  | slot_count | action |
  +---------------------+------------------------+-----------------+--------+------------+--------+
  | 2023-07-20 19:30:27 | 12954109101902401697   | ANNUAL          | ACTIVE |        100 | CREATE |
  | 2023-07-27 22:29:21 | 11445583810276646822   | FLEX            | ACTIVE |        100 | CREATE |
  | 2023-07-27 23:10:06 | 7341455530498381779    | MONTHLY         | ACTIVE |        100 | CREATE |
  | 2023-07-27 23:11:06 | 7341455530498381779    | FLEX            | ACTIVE |        100 | UPDATE |
  | 2023-07-27 23:11:06 | 7341455530498381779    | MONTHLY         | ACTIVE |        100 | DELETE |
  */

  capacity_changes_with_additional_deleted_event_for_changed_plan AS (
    SELECT
      next_plan_change_timestamp AS change_timestamp,
      project_id,
      project_number,
      capacity_commitment_id,
      commitment_plan,
      state,
      slot_count,
      'DELETE' AS action,
      commitment_start_time,
      commitment_end_time,
      failure_status,
      renewal_plan,
      user_email,
      edition,
      is_flat_rate,
    FROM commitments_with_next_plan
    WHERE commitment_plan <> next_plan
    UNION ALL
    SELECT * FROM `region-us.INFORMATION_SCHEMA.CAPACITY_COMMITMENT_CHANGES_BY_PROJECT`
  ),

  /*
  The committed_slots change the history. For example:
  +---------------------+------------------------+------------------+-----------------+
  |  change_timestamp   | capacity_commitment_id | slot_count_delta | commitment_plan |
  +---------------------+------------------------+------------------+-----------------+
  | 2023-07-20 19:30:27 | 12954109101902401697   |              100 | ANNUAL          |
  | 2023-07-27 22:29:21 | 11445583810276646822   |              100 | FLEX            |
  | 2023-07-27 23:10:06 | 7341455530498381779    |              100 | MONTHLY         |
  | 2023-07-27 23:11:06 | 7341455530498381779    |             -100 | MONTHLY         |
  | 2023-07-27 23:11:06 | 7341455530498381779    |              100 | FLEX            |
  */

  capacity_commitment_slot_data AS (
    SELECT
      change_timestamp,
      capacity_commitment_id,
      CASE
        WHEN action = "CREATE" OR action = "UPDATE"
          THEN
            IFNULL(
              IF(
                LAG(action)
                  OVER (
                    PARTITION BY capacity_commitment_id ORDER BY change_timestamp ASC, action ASC
                  )
                  IN UNNEST(['CREATE', 'UPDATE']),
                slot_count - LAG(slot_count)
                  OVER (
                    PARTITION BY capacity_commitment_id ORDER BY change_timestamp ASC, action ASC
                  ),
                slot_count),
              slot_count)
        ELSE
          IF(
            LAG(action)
              OVER (PARTITION BY capacity_commitment_id ORDER BY change_timestamp ASC, action ASC)
              IN UNNEST(['CREATE', 'UPDATE']),
            -1 * slot_count,
            0)
        END
        AS slot_count_delta,
      commitment_plan
    FROM
      capacity_changes_with_additional_deleted_event_for_changed_plan
    WHERE
      state = "ACTIVE"
      AND edition = edition_to_check
      AND change_timestamp <= end_time
  ),

  /*
  The total_committed_slots history for each plan. For example:
  +---------------------+---------------+-----------------+
  |  change_timestamp   | capacity_slot | commitment_plan |
  +---------------------+---------------+-----------------+
  | 2023-07-20 19:30:27 |           100 | ANNUAL          |
  | 2023-07-27 22:29:21 |           100 | FLEX            |
  | 2023-07-27 23:10:06 |           100 | MONTHLY         |
  | 2023-07-27 23:11:06 |             0 | MONTHLY         |
  | 2023-07-27 23:11:06 |           200 | FLEX            |
  */

  running_capacity_commitment_slot_data AS (
    SELECT
      change_timestamp,
      SUM(slot_count_delta)
        OVER (
          PARTITION BY commitment_plan
          ORDER BY change_timestamp
          RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
        )
        AS capacity_slot,
      commitment_plan,
    FROM
      capacity_commitment_slot_data
  ),

  /*

  The slot_seconds between each changes, partitioned by each plan. For example:
  +---------------------+--------------+-----------------+
  |  change_timestamp   | slot_seconds | commitment_plan |
  +---------------------+--------------+-----------------+
  | 2023-07-20 19:30:27 |     64617300 | ANNUAL          |
  | 2023-07-27 22:29:21 |       250500 | FLEX            |
  | 2023-07-27 23:10:06 |         6000 | MONTHLY         |
  | 2023-07-27 23:11:06 |            0 | MONTHLY         |
  | 2023-07-27 23:11:06 |      5626800 | FLEX            |
  */

  slot_seconds_data AS (
    SELECT
      change_timestamp,
      GetSlotSecondsBetweenChanges(
        capacity_slot,
        UNIX_MILLIS(change_timestamp),
        UNIX_MILLIS(
          IFNULL(
            LEAD(change_timestamp)
              OVER (PARTITION BY commitment_plan ORDER BY change_timestamp ASC),
            CURRENT_TIMESTAMP())),
        UNIX_MILLIS(start_time),
        UNIX_MILLIS(end_time)) AS slot_seconds,
      commitment_plan,
    FROM
      running_capacity_commitment_slot_data
    WHERE
      change_timestamp <= end_time
  )

/*

The final result is similar to the following:
+-----------------+--------------------+
| commitment_plan | total_slot_seconds |
+-----------------+--------------------+
| ANNUAL          |           64617300 |
| MONTHLY         |               6000 |
| FLEX            |            5877300 |
*/

SELECT
  commitment_plan,
  SUM(slot_seconds) AS total_slot_seconds
FROM
  slot_seconds_data
GROUP BY
  commitment_plan

次のスクリプトは、特定のエディションのコミットメントの対象外のスロット使用量を確認します。この使用量には、スケーリングされたスロットと、コミットメントの対象外であるベースライン スロットという 2 種類のスロットが含まれます。

開いて対象外シナリオのスクリプトを表示する

/*
This script has several parts:
1. Calculate the baseline and scaled slots for reservations
2. Calculate the committed slots
3. Join the two results above to calculate the baseline not covered by committed
   slots
4. Aggregate the number
*/

-- variables
DECLARE start_time, end_time TIMESTAMP;

DECLARE
  edition_to_check STRING;

/* Google uses Pacific Time to calculate the billing period for all customers,
regardless of their time zone. Use the following format if you want to match the
billing report. Change the start_time and end_time values to match the desired
window. */

/* The following three variables (start_time, end_time, and edition_to_check)
are the only variables that you need to set in the script.

During daylight savings time, the start_time and end_time variables should
follow this format: 2024-02-20 00:00:00-08. */

SET start_time = "2023-07-20 00:00:00-07";
SET end_time = "2023-07-28 00:00:00-07";
SET edition_to_check = 'ENTERPRISE';

/*
The following function returns the slot seconds for the time window between
two capacity changes. For example, if there are 100 slots between (2023-06-01
10:00:00, 2023-06-01 11:00:00), then during that window the total slot seconds
will be 100 * 3600.

This script calculates a specific window (based on the variables defined above),
which is why the following script includes script_start_timestamp_unix_millis
and script_end_timestamp_unix_millis. */

CREATE TEMP FUNCTION GetSlotSecondsBetweenChanges(
  slots FLOAT64,
  range_begin_timestamp_unix_millis FLOAT64,
  range_end_timestamp_unix_millis FLOAT64,
  script_start_timestamp_unix_millis FLOAT64,
  script_end_timestamp_unix_millis FLOAT64)
RETURNS INT64
LANGUAGE js
AS r"""
    if (script_end_timestamp_unix_millis < range_begin_timestamp_unix_millis || script_start_timestamp_unix_millis > range_end_timestamp_unix_millis) {
      return 0;
    }
    var begin = Math.max(script_start_timestamp_unix_millis, range_begin_timestamp_unix_millis)
    var end = Math.min(script_end_timestamp_unix_millis, range_end_timestamp_unix_millis)
    return slots * Math.ceil((end - begin) / 1000.0)
""";
/*
Sample RESERVATION_CHANGES data (unrelated columns ignored):
+---------------------+------------------+--------+---------------+---------------+
|  change_timestamp   | reservation_name | action | slot_capacity | current_slots |
+---------------------+------------------+--------+---------------+---------------+
| 2023-07-27 22:24:15 | res1             | CREATE |           300 |             0 |
| 2023-07-27 22:25:21 | res1             | UPDATE |           300 |           180 |
| 2023-07-27 22:39:14 | res1             | UPDATE |           300 |           100 |
| 2023-07-27 22:40:20 | res2             | CREATE |           300 |             0 |
| 2023-07-27 22:54:18 | res2             | UPDATE |           300 |           120 |
| 2023-07-27 22:55:23 | res1             | UPDATE |           300 |             0 |

Sample CAPACITY_COMMITMENT_CHANGES data (unrelated columns ignored):
+---------------------+------------------------+-----------------+--------+------------+--------+
|  change_timestamp   | capacity_commitment_id | commitment_plan | state  | slot_count | action |
+---------------------+------------------------+-----------------+--------+------------+--------+
| 2023-07-20 19:30:27 | 12954109101902401697   | ANNUAL          | ACTIVE |        100 | CREATE |
| 2023-07-27 22:29:21 | 11445583810276646822   | FLEX            | ACTIVE |        100 | CREATE |
| 2023-07-27 23:10:06 | 7341455530498381779    | MONTHLY         | ACTIVE |        100 | CREATE |
*/

WITH
  /*
  The scaled_slots & baseline change history:
  +---------------------+------------------+------------------------------+---------------------+
  |  change_timestamp   | reservation_name | autoscale_current_slot_delta | baseline_slot_delta |
  +---------------------+------------------+------------------------------+---------------------+
  | 2023-07-27 22:24:15 | res1             |                            0 |                 300 |
  | 2023-07-27 22:25:21 | res1             |                          180 |                   0 |
  | 2023-07-27 22:39:14 | res1             |                          -80 |                   0 |
  | 2023-07-27 22:40:20 | res2             |                            0 |                 300 |
  | 2023-07-27 22:54:18 | res2             |                          120 |                   0 |
  | 2023-07-27 22:55:23 | res1             |                         -100 |                   0 |
  */
  reservation_slot_data AS (
    SELECT
      change_timestamp,
      reservation_name,
      CASE action
        WHEN "CREATE" THEN autoscale.current_slots
        WHEN "UPDATE"
          THEN
            IFNULL(
              autoscale.current_slots - LAG(autoscale.current_slots)
                OVER (
                  PARTITION BY project_id, reservation_name
                  ORDER BY change_timestamp ASC, action ASC
                ),
              IFNULL(
                autoscale.current_slots,
                IFNULL(
                  -1 * LAG(autoscale.current_slots)
                    OVER (
                      PARTITION BY project_id, reservation_name
                      ORDER BY change_timestamp ASC, action ASC
                    ),
                  0)))
        WHEN "DELETE"
          THEN
            IF(
              LAG(action)
                OVER (
                  PARTITION BY project_id, reservation_name
                  ORDER BY change_timestamp ASC, action ASC
                )
                IN UNNEST(['CREATE', 'UPDATE']),
              -1 * autoscale.current_slots,
              0)
        END
        AS autoscale_current_slot_delta,
      CASE action
        WHEN "CREATE" THEN slot_capacity
        WHEN "UPDATE"
          THEN
            IFNULL(
              slot_capacity - LAG(slot_capacity)
                OVER (
                  PARTITION BY project_id, reservation_name
                  ORDER BY change_timestamp ASC, action ASC
                ),
              IFNULL(
                slot_capacity,
                IFNULL(
                  -1 * LAG(slot_capacity)
                    OVER (
                      PARTITION BY project_id, reservation_name
                      ORDER BY change_timestamp ASC, action ASC
                    ),
                  0)))
        WHEN "DELETE"
          THEN
            IF(
              LAG(action)
                OVER (
                  PARTITION BY project_id, reservation_name
                  ORDER BY change_timestamp ASC, action ASC
                )
                IN UNNEST(['CREATE', 'UPDATE']),
              -1 * slot_capacity,
              0)
        END
        AS baseline_slot_delta,
    FROM
      `region-us.INFORMATION_SCHEMA.RESERVATION_CHANGES`
    WHERE
      edition = edition_to_check
      AND change_timestamp <= end_time
  ),

  -- Convert the above to running total
  /*
  +---------------------+-------------------------+----------------+
  |  change_timestamp   | autoscale_current_slots | baseline_slots |
  +---------------------+-------------------------+----------------+
  | 2023-07-27 22:24:15 |                       0 |            300 |
  | 2023-07-27 22:25:21 |                     180 |            300 |
  | 2023-07-27 22:39:14 |                     100 |            300 |
  | 2023-07-27 22:40:20 |                     100 |            600 |
  | 2023-07-27 22:54:18 |                     220 |            600 |
  | 2023-07-27 22:55:23 |                     120 |            600 |
  */
  running_reservation_slot_data AS (
    SELECT
      change_timestamp,
      SUM(autoscale_current_slot_delta)
        OVER (ORDER BY change_timestamp RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
        AS autoscale_current_slots,
      SUM(baseline_slot_delta)
        OVER (ORDER BY change_timestamp RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
        AS baseline_slots,
    FROM
      reservation_slot_data
  ),

  /*
  The committed_slots change history. For example:
  +---------------------+------------------------+------------------+
  |  change_timestamp   | capacity_commitment_id | slot_count_delta |
  +---------------------+------------------------+------------------+
  | 2023-07-20 19:30:27 | 12954109101902401697   |              100 |
  | 2023-07-27 22:29:21 | 11445583810276646822   |              100 |
  | 2023-07-27 23:10:06 | 7341455530498381779    |              100 |
  */
  capacity_commitment_slot_data AS (
    SELECT
      change_timestamp,
      capacity_commitment_id,
      CASE
        WHEN action = "CREATE" OR action = "UPDATE"
          THEN
            IFNULL(
              IF(
                LAG(action)
                  OVER (
                    PARTITION BY capacity_commitment_id ORDER BY change_timestamp ASC, action ASC
                  )
                  IN UNNEST(['CREATE', 'UPDATE']),
                slot_count - LAG(slot_count)
                  OVER (
                    PARTITION BY capacity_commitment_id ORDER BY change_timestamp ASC, action ASC
                  ),
                slot_count),
              slot_count)
        ELSE
          IF(
            LAG(action)
              OVER (PARTITION BY capacity_commitment_id ORDER BY change_timestamp ASC, action ASC)
              IN UNNEST(['CREATE', 'UPDATE']),
            -1 * slot_count,
            0)
        END
        AS slot_count_delta
    FROM
      `region-us.INFORMATION_SCHEMA.CAPACITY_COMMITMENT_CHANGES_BY_PROJECT`
    WHERE
      state = "ACTIVE"
      AND edition = edition_to_check
      AND change_timestamp <= end_time
  ),

  /*
  The total_committed_slots history. For example:
  +---------------------+---------------+
  |  change_timestamp   | capacity_slot |
  +---------------------+---------------+
  | 2023-07-20 19:30:27 |           100 |
  | 2023-07-27 22:29:21 |           200 |
  | 2023-07-27 23:10:06 |           300 |
  */
  running_capacity_commitment_slot_data AS (
    SELECT
      change_timestamp,
      SUM(slot_count_delta)
        OVER (ORDER BY change_timestamp RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
        AS capacity_slot
    FROM
      capacity_commitment_slot_data
  ),

  /* Add next_change_timestamp to the above data,
   which will be used when joining with reservation data. For example:
  +---------------------+-----------------------+---------------+
  |  change_timestamp   | next_change_timestamp | capacity_slot |
  +---------------------+-----------------------+---------------+
  | 2023-07-20 19:30:27 |   2023-07-27 22:29:21 |           100 |
  | 2023-07-27 22:29:21 |   2023-07-27 23:10:06 |           200 |
  | 2023-07-27 23:10:06 |   2023-07-31 00:14:37 |           300 |
  */
  running_capacity_commitment_slot_data_with_next_change AS (
    SELECT
      change_timestamp,
      IFNULL(LEAD(change_timestamp) OVER (ORDER BY change_timestamp ASC), CURRENT_TIMESTAMP())
        AS next_change_timestamp,
      capacity_slot
    FROM
      running_capacity_commitment_slot_data
  ),

  /*
  Whenever we have a change in reservations or commitments,
  the scaled_slots_and_baseline_not_covered_by_commitments will be changed.
  Hence we get a collection of all the change_timestamp from both tables.
  +---------------------+
  |  change_timestamp   |
  +---------------------+
  | 2023-07-20 19:30:27 |
  | 2023-07-27 22:24:15 |
  | 2023-07-27 22:25:21 |
  | 2023-07-27 22:29:21 |
  | 2023-07-27 22:39:14 |
  | 2023-07-27 22:40:20 |
  | 2023-07-27 22:54:18 |
  | 2023-07-27 22:55:23 |
  | 2023-07-27 23:10:06 |
  */
  merged_timestamp AS (
    SELECT
      change_timestamp
    FROM
      running_reservation_slot_data
    UNION DISTINCT
    SELECT
      change_timestamp
    FROM
      running_capacity_commitment_slot_data
  ),

  /*
  Change running reservation-slots and make sure we have one row when commitment changes.
  +---------------------+-------------------------+----------------+
  |  change_timestamp   | autoscale_current_slots | baseline_slots |
  +---------------------+-------------------------+----------------+
  | 2023-07-20 19:30:27 |                       0 |              0 |
  | 2023-07-27 22:24:15 |                       0 |            300 |
  | 2023-07-27 22:25:21 |                     180 |            300 |
  | 2023-07-27 22:29:21 |                     180 |            300 |
  | 2023-07-27 22:39:14 |                     100 |            300 |
  | 2023-07-27 22:40:20 |                     100 |            600 |
  | 2023-07-27 22:54:18 |                     220 |            600 |
  | 2023-07-27 22:55:23 |                     120 |            600 |
  | 2023-07-27 23:10:06 |                     120 |            600 |
  */
  running_reservation_slot_data_with_merged_timestamp AS (
    SELECT
      change_timestamp,
      IFNULL(
        autoscale_current_slots,
        IFNULL(
          LAST_VALUE(autoscale_current_slots IGNORE NULLS) OVER (ORDER BY change_timestamp ASC), 0))
        AS autoscale_current_slots,
      IFNULL(
        baseline_slots,
        IFNULL(LAST_VALUE(baseline_slots IGNORE NULLS) OVER (ORDER BY change_timestamp ASC), 0))
        AS baseline_slots
    FROM
      running_reservation_slot_data
    RIGHT JOIN
      merged_timestamp
      USING (change_timestamp)
  ),

  /*
  Join the above, so that we will know the number for baseline not covered by commitments.
  +---------------------+-----------------------+-------------------------+------------------------------------+
  |  change_timestamp   | next_change_timestamp | autoscale_current_slots | baseline_not_covered_by_commitment |
  +---------------------+-----------------------+-------------------------+------------------------------------+
  | 2023-07-20 19:30:27 |   2023-07-27 22:24:15 |                       0 |                                  0 |
  | 2023-07-27 22:24:15 |   2023-07-27 22:25:21 |                       0 |                                200 |
  | 2023-07-27 22:25:21 |   2023-07-27 22:29:21 |                     180 |                                200 |
  | 2023-07-27 22:29:21 |   2023-07-27 22:39:14 |                     180 |                                100 |
  | 2023-07-27 22:39:14 |   2023-07-27 22:40:20 |                     100 |                                100 |
  | 2023-07-27 22:40:20 |   2023-07-27 22:54:18 |                     100 |                                400 |
  | 2023-07-27 22:54:18 |   2023-07-27 22:55:23 |                     220 |                                400 |
  | 2023-07-27 22:55:23 |   2023-07-27 23:10:06 |                     120 |                                400 |
  | 2023-07-27 23:10:06 |   2023-07-31 00:16:07 |                     120 |                                300 |
  */
  scaled_slots_and_baseline_not_covered_by_commitments AS (
    SELECT
      r.change_timestamp,
      IFNULL(LEAD(r.change_timestamp) OVER (ORDER BY r.change_timestamp ASC), CURRENT_TIMESTAMP())
        AS next_change_timestamp,
      r.autoscale_current_slots,
      IF(
        r.baseline_slots - IFNULL(c.capacity_slot, 0) > 0,
        r.baseline_slots - IFNULL(c.capacity_slot, 0),
        0) AS baseline_not_covered_by_commitment
    FROM
      running_reservation_slot_data_with_merged_timestamp r
    LEFT JOIN
      running_capacity_commitment_slot_data_with_next_change c
      ON
        r.change_timestamp >= c.change_timestamp
        AND r.change_timestamp < c.next_change_timestamp
  ),

  /*
  The slot_seconds between each changes. For example:
  +---------------------+--------------------+
  |  change_timestamp   | slot_seconds |
  +---------------------+--------------+
  | 2023-07-20 19:30:27 |            0 |
  | 2023-07-27 22:24:15 |        13400 |
  | 2023-07-27 22:25:21 |        91580 |
  | 2023-07-27 22:29:21 |       166320 |
  | 2023-07-27 22:39:14 |        13200 |
  | 2023-07-27 22:40:20 |       419500 |
  | 2023-07-27 22:54:18 |        40920 |
  | 2023-07-27 22:55:23 |       459160 |
  | 2023-07-27 23:10:06 |     11841480 |
  */
  slot_seconds_data AS (
    SELECT
      change_timestamp,
      GetSlotSecondsBetweenChanges(
        autoscale_current_slots + baseline_not_covered_by_commitment,
        UNIX_MILLIS(change_timestamp),
        UNIX_MILLIS(next_change_timestamp),
        UNIX_MILLIS(start_time),
        UNIX_MILLIS(end_time)) AS slot_seconds
    FROM
      scaled_slots_and_baseline_not_covered_by_commitments
    WHERE
      change_timestamp <= end_time AND next_change_timestamp > start_time
  )

/*
Final result for this example:
+--------------------+
| total_slot_seconds |
+--------------------+
|           13045560 |
*/
SELECT
  SUM(slot_seconds) AS total_slot_seconds
FROM
  slot_seconds_data

ジョブ パフォーマンスをモニタリングする

費用の増加を回避するために、自動スケーリングの max_slots の調整が必要になる場合があります。次のクエリは、ジョブのパフォーマンスに関するコンテキストを提供します。これにより、ワークロードに適切な数の自動スケーリング スロットを選択できます。

次のクエリは、過去のジョブのパフォーマンスに関する予約の詳細を示します。

SELECT
    AVG(TIMESTAMP_DIFF(end_time, creation_time, MILLISECOND)) as avg_latency_ms,
    SUM(total_bytes_processed) as total_bytes,
    COUNT(*) as query_numbers,
FROM
    `PROJECT_ID.region-REGION_NAME`.INFORMATION_SCHEMA.JOBS_BY_ORGANIZATION
WHERE creation_time >= START_TIME
    AND creation_time < END_TIME
    AND (statement_type != "SCRIPT" OR statement_type IS NULL)
    AND reservation_id = RESERVATION_ID

次のように置き換えます。

  • PROJECT_ID: プロジェクトの ID
  • REGION_NAME: プロジェクトのリージョン
  • START_TIME: データの表示を開始する作成日時
  • END_TIME: データの表示を終了する作成日時
  • RESERVATION_ID: 予約 ID

次の例では、5 日間のジョブの詳細を取得します。

SELECT
    AVG(TIMESTAMP_DIFF(end_time, creation_time, MILLISECOND)) as avg_latency_ms,
    SUM(total_bytes_processed) as total_bytes,
    COUNT(*) as query_numbers,
FROM
    `myproject.region-us`.INFORMATION_SCHEMA.JOBS_BY_ORGANIZATION
WHERE creation_time >= '2024-06-25 00:00:00-07'
    AND creation_time < '2024-06-30 00:00:00-07'
    AND (statement_type != "SCRIPT" OR statement_type IS NULL)
    AND reservation_id = reservationID

割り当て

最大予約サイズの合計がスロット割り当てを超えないようにしてください。

割り当てについては、割り当てと上限をご覧ください。

次のステップ