BigQuery コンピューティング容量（スロット）ベースの課金体系とスロット予約の特性を理解する

::: note info
これは [ZOZO Advent Calendar 2024](https://qiita.com/advent-calendar/2024/zozo) カレンダー Vol.9 の 9日目の記事です。
:::

# はじめに
本記事では、BigQueryにおけるコンピューティング容量（以下スロット）ベースの課金について課金体系やスロット周りの用語、スロット予約の特性についてご説明します。

BigQueryの分析費用の見直しにあたり、用語や挙動（特性）など混乱するところが多かったため自分の理解をまとめた資料になります。BigQueryのスロットベースな課金体系についてキャッチアップする際、役に立てば幸いです。

::: note warn
スロットベースの課金体系については常に最新版の公式ドキュメントを参照するように注意してください。以前内容に変更があったため、同じ名称で概念の異なるものがあったりします。
::: 

# コンピューティング容量（スロット）とは
スロットとは、BigQueryにおける計算リソースを指します。（仮想CPU + メモリ + ディスク）

# オンデマンド vs コンピューティング容量（スロット）ベースの課金方法の違い
BigQueryの料金には主に、分析料金とストレージ料金の2種類があります。オンデマンド vs スロットベースはこのうち、分析料金に関わるプランです。
デフォルトではオンデマンドが適用されます。

- オンデマンド
  - クエリ実行時にスキャンしたデータに対して課金される
- スロットベース
  - クエリ実行時に確保されたスロットに対して課金される

両者の主な違いは課金対象にあります。

オンデマンドがスキャンしたデータに対して課金されるのに対し、スロットベースでは使用した計算リソースに対して課金されます。
つまり実行するクエリが同じでも選択する料金プランによって、コストを最適化できるということです。

例えば、以下のような2種類のクエリがあったとします。

**① 計算量が多いため利用するスロットは多いが、スキャン対象のデータ量は少ないケース**

```sql
SELECT
   Window関数等の複雑な計算
FROM
   小さいテーブル_1
CROSS JOIN
   小さいテーブル_2
CROSS JOIN
   小さいテーブル_3
```

**② スキャンするデータ量は多いが、計算量が少ないため利用するスロットは少ないケース**

```sql
SELECT
   *
FROM
   巨大なテーブル
```

①のケースのように計算リソース（スロット）に対する費用が支配的である場合は、データ量に対する課金プランであるオンデマンドを利用した方が費用対効果が高いかと思います。

一方で、②のケースのようにスキャンしたデータに対する費用が支配的である場合は、計算リソースに対する課金プランであるスロットベースを利用した方が費用対効果が高いかと思います。

# コンピューティング容量（スロット）ベースの課金体系と種類（BigQuery Editions）
前述の通り、スロットベースの課金体系の場合、クエリ実行時に確保されたスロットに対して課金されます。

**最小課金時間は1分となっており、最小50スロット単位でスケールアウトします。（参考：[自動スケーリングの予約を使用する](https://cloud.google.com/bigquery/docs/slots-autoscaling-intro?hl=ja)）**

またスロットベースの課金体系では、スロットと分析容量（クエリ実行時に利用されるスロット）を明示的に制御可能ですが、オンデマンドではできません。

スロットベースの課金体系（[BigQuery Editions](https://cloud.google.com/bigquery/docs/editions-intro?hl=ja)）には複数の種類が用意されています。
- Standard Edition
- Enterprise Edition
- Enterprise Plus Edition

行・列レベルでのアクセス制御があるテーブルを対象にクエリ実行する場合、Standard Editionでは対応していません。そのためEnterpriseまたはEnterprise Plus Editionを利用する必要があることに注意してください。
各Editionsの機能の違いの詳細は、公式ドキュメントの[エディションの機能](https://cloud.google.com/bigquery/docs/editions-intro?hl=ja#editions_features)を参照してください。

# コンピューティング容量（スロット）周辺の用語
スロット周りのドキュメントを読む際にいくつか頻出の用語があります。
自分は当初それぞれの違いで混乱するところがありました。前提知識として概要を知っておくと公式ドキュメントを読みやすくなりそうな用語をピックアップしてご紹介します。

### スロット
BigQueryの仮想CPU（+ メモリ・ディスク）のこと。

### 予約
事前に使用できるスロットの上限やベースラインを作成する仕組み。プロジェクト・フォルダ・組織を作成した予約に割り当てることで、購入したスロットを使用できます。  
前述の通りスロットは、作成した予約ごとに最小50スロット単位でオートスケールし1分単位で課金されます。上限やベースライン、コミットメントはオプションとして指定できます。

### BigQuery Reservation API
BigQueryでSlot課金を利用する機能のこと。BigQuery Resercation APIを使用することでSlotについてコミットメントや予約を作成し、プロジェクト・フォルダ・組織に割り当てできます。

### オートスケール
ワークロードの需要に合わせて割り当てられるスロットを動的に調整すること。

### ベースライン
予約の作成時にオプションとして指定することで、利用可能なスロットを事前に確保しておくこと。

参考：[ベースライン スロットと自動スケーリング スロットとともに予約を使用する](https://cloud.google.com/bigquery/docs/slots-autoscaling-intro?hl=ja#using_reservations_with_baseline_and_autoscaling_slots)

### コミットメント
予約の作成時にオプションとして指定することで、1年 or 3年の固定された期間に100スロット単位でスロットを購入する方法。
購入したスロット分は定常的に課金されるが、通常オートスケールして利用されるスロットより1分あたりの単価は安くなります。

# コンピューティング容量（スロット）の予約
前述の通りスロットの予約とは、事前に使用できるスロットの上限やベースラインを作成する仕組みです。

スロットの予約（BigQuery Reservationsリソース）は1つの管理プロジェクトにより所有され、管理プロジェクトでは管理プロジェクトと同じ[組織リソース](https://cloud.google.com/resource-manager/docs/cloud-platform-resource-hierarchy?hl=ja#organizations)内のプロジェクトを予約に割り当てることができます。

例として、`bigquery-reservation-control`という名前のプロジェクトを予約の管理プロジェクトとした場合を考えます。
管理プロジェクトでは複数の予約を作成でき、予約ごとに異なるフォルダやプロジェクトを割り当てることができます。ここでは、組織内の各チームごとに予約を作成し、各チームが管理するプロジェクトをそれぞれの予約に割り当てているとします。

次図はこの例における管理プロジェクトの予約作成と割り当てのイメージです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/576866/408a4050-5e6a-a653-ca82-6924de8728a4.png)

スロットの予約時には、スロットがオートスケール可能な最大値を指定できます。ここで指定した最大値がそれぞれの予約で同時に利用可能なスロットの上限ということになります。

予約ごとに割り当てられたプロジェクトは同一予約内で使用可能なスロットを共有するという点を意識しておくとスロット消費をイメージしやすいかと思います。
ちなみにオンデマンド課金の場合にもスロットが無限にスケールアウトするわけではなく、プロジェクト内で2,000スロットが同時実行の上限となっています。

## コンピューティング容量（スロット）不足時の挙動
予約ごとに割り当てられたプロジェクトは同一予約内で使用可能なスロットを共有すると前述しました。
この場合の懸念点は同時利用するスロットが、割り当ての上限に達した場合の挙動かと思います。

リクエストに対して利用可能なスロットが不足する場合は、スロットがキューに入り、リクエストに対して均等に50スロットずつが割り当てられるという挙動になります。
そのため、同一の予約に割り当てられたプロジェクトでは、十分にスケールアウトが行えずクエリ実行に遅延が発生する可能性があるため注意してください。

## コスト効率の良いコンピュート容量（スロット）の割り当て
スロットは50スロット単位でオートスケールし、1分単位で課金されると上述しました。
つまり50スロット単位で課金される特性上、1スロットしか利用しない場合でも50スロット単位でオートスケールしてしまい、49スロット分は費用が発生することになります。

このような余剰分スロットをアイドルスロットと呼びます。例えば、次のようなケースでスロットはアイドル状態となります。
- 利用されていないスロットコミットメント
- ベースラインスロットに割り当てられているが、利用されていないスロット
- オートスケールにより購入済みだが、利用されていないスロット

スロット利用時のコスト効率を高めるには、このようなアイドル状態のスロットを減らすことが効果的です。

コミットメントやベースラインについては導入の有無自体がユースケースごとに変わりそうですが、オートスケールについては基本的にどんなユースケースにおいても考慮が必要になる点かと思います。

予約内でスロットの最大値を指定すると、最大値を超えてのオートスケールはできなくなります。

予約内でオートスケール可能なスロットを使い切った状態をスロット不足と前述していました。一方でこの状態は予約内で利用可能なスロットをフルに使っている状態になるため、アイドル状態のスロットは発生しません。そのため、ある意味最もコスト効率の良い状態と言えます。

次図はGoogle Cloudのコンソールから確認できるスロット使用量のモニタリング画面の例になります。ここでは単一の予約内におけるスロットの使用量を表示しています。

![スクリーンショット 2024-12-01 14.12.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/576866/b63c71b6-acae-5f84-d1ec-f491bc651fb1.png)

この例の予約では、スロットの最大値を100に指定しており、コミットメントやベースラインは指定していません。

見辛くて恐縮ですが、100の値に引かれているグリーンの線が`Maximum Capacity`を表します。
またオレンジの線が`Baseline + Autoscale Capacity`です。`Baseline + Autoscale Capacity`はその時点で確保されたスロットであり、1分単位でスロット数に応じて費用が発生します。
ピンクの線が`Total Usage`を表し、クエリ実行により使用されているスロットになります。

つまり`Baseline + Autoscale Capacity`の線と`Total Usage`の線の差分がアイドルスロットを表しており、コスト効率を高めるためにはこちらの差分を最小化する必要があるということがわかります。

`Maximum Capacity`を下げると、オートスケールするスロットが頭打ちになりこの差分を小さくできることがわかります。
一方で、`Maximum Capacity`を下げると前述したスロット不足のリスクも増えるため、コスト効率とパフォーマンスはトレードオフであるということに注意してください。

スロットの予約は複数作成できるため、実行されるクエリの特性によって分類し、適切な値を設定した予約に割り当てることでコスト効率を高めることができます。

# まとめ
本記事では、スロットベースの課金について課金体系や用語、スロット予約の特性的な面について簡単にご説明しました。本記事がこの辺りの概要を把握するのに役立てば幸いです。

またここでは詳しく紹介しませんでしたが、オンデマンド vs コンピューティング容量ベースの料金モデルでどちらを利用すると費用が安くなるかについては、既存プロジェクト内でのクエリ実行によりスキャンされているデータ量やスロット量により見積もり可能です。
これらのデータは[`INFORMATION_SCHEMA`](https://cloud.google.com/bigquery/docs/information-schema-intro?hl=ja)ビューから取得可能であるため、ご参照ください。

# 参考
- [BigQueryを使った集計バッチの費用をActive Metadata Managementの知見を活かして4割減らした話](https://techblog.zozo.com/entry/switch-pricing-model)
- [window関数を使いこなす 〜分析のためのSQL〜](https://qiita.com/HiromuMasuda0228/items/0b20d461f1a80bd30cfc)
- [スロットについて](https://cloud.google.com/bigquery/docs/slots?hl=ja)
- [BigQuery の料金](https://cloud.google.com/bigquery/pricing?hl=ja#flat_rate_pricing)