BigQueryの料金体系をサクッと解説

# はじめに

こんにちは、京セラコミュニケーションシステム　西田（@kccs_hiromi-nishida）です。

みなさんは、「色んなデータを溜めて分析するのにBigQueryが良さそうだけど、一体どのくらいのコストがかかるんだろう、手っ取り早く知りたい！」と思ったことはありませんか？

この記事ではBigQueryを利用するにあたって、何に・どれくらいのコストがかかるのかを解説します。

:::note info
本記事は2022年8月ごろに作成しております。よって、引用している文章などはこの時点での最新となります。ご了承ください。
:::

:::note warn
2023年7月5日にBigQueryの料金体系が改定されます。
本記事の料金体系に関する記載内容は料金改定前のものです。
新料金体系に関する記事は以下の記事にまとめているので、ぜひご覧ください。
:::

https://qiita.com/kccs_hiromi-nishida/items/933ca11768112590f9a7


## この記事の対象者

- BigQueryを利用してみたいけれど、料金体系がよくわからない方
- 何にどれだけの料金がかかるのか知りたい方

## BigQueryの料金は大きく分けて2つ

BigQueryの料金には大きく分けて以下の2つがあります。

1. ストレージ料金
2. 分析料金

※上記2つのほかに、ストリーミング挿入やBigQuery Storage APIなど特定の操作に応じて料金が発生します。これらの料金に関しては公式ドキュメントを参照してください

:::note info
［参照］公式ドキュメント
  [データ取り込みの料金](https://cloud.google.com/bigquery/pricing#data_ingestion_pricing)
  [データ抽出の料金](https://cloud.google.com/bigquery/pricing#data_extraction_pricing)
:::

### 1.ストレージ料金について

ストレージ料金とは、BigQueryに読み込むデータを保存する費用です。保存期間・データ量によって費用が変動し、アクティブストレージと長期保存の2種類があります。

- **アクティブストレージ**</br>
  過去90日間で変更されたテーブルやパーティション

- **長期保存**</br>
  90日間連続して変更されていないテーブルまたはテーブル パーティションで、そのテーブルのストレージの料金は自動的に約50% 値引きされる

以下は東京（asia-northeast1）の料金例（2022年8月時点）となります。（1$=130円で算出）

| オペレーション       | 料金                   | 備考                |
| -------------------- | ---------------------- | ------------------- |
| アクティブストレージ | $0.023 (約2.99円) / GB | 毎月 10 GB まで無料 |
| 長期保存             | $0.016 (約2.08円) / GB | 毎月 10 GB まで無料 |

:::note info
［参照］公式ドキュメント
  [ストレージの料金](https://cloud.google.com/bigquery/pricing#storage)
:::

### 2.分析料金について

分析料金には、オンデマンド分析と定額料金、2つの料金モデルがあり、それぞれ単体でも組み合わせても利用が可能です。

#### 2-1.オンデマンド分析

データをBigQueryに保存するか、Cloud Storageやドライブ等の外部データソースに保存するかに関係なく、クエリが処理したバイト数に基づいて料金が加算されます。

※外部データが別のGoogle Cloudプロダクトに保存されている場合は、そのプロダクトのストレージ料金が別途適用

以下は東京（asia-northeast1）の料金例（2022年8月時点）です。（1$=130円で算出）

| オペレーション       | 料金                 | 備考             |
| -------------------- | -------------------- | ---------------- |
| クエリ(オンデマンド) | $6.00 (約780円) / TB | 毎月 1TBまで無料 |

オンデマンド分析料金の注意点の中から2つピックアップして紹介したいと思います。

:::note info
［参照］公式ドキュメント
  [オンデマンド分析の料金](https://cloud.google.com/bigquery/pricing#on_demand_pricing)
:::

##### ①BigQueryはカラム型データ構造

>BigQuery ではカラム型データ構造が使用されています。選択した列で処理されたデータの合計容量に応じて課金されます。

上記内容から、”カラム型データ構造なので必要なカラムのデータだけ取得する” というのが料金に関して押さえておきたいポイントです。
このポイントを踏まえたうえで、以下3項目を確認してみます。

###### * (アスタリスク指定)

アスタリスク指定によるデータの取得は対象テーブルの全カラムを対象にするため、<font color="#ff0000">全カラム分のコストがかかります。</font>
試しに、新型コロナウイルスの一般公開データセット内のテーブルをアスタリスク指定で取得してみると、11.13GBとなりました。
![img1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2752159/0d7df2d0-89e3-3c19-d2e0-bc4548f89b1a.png "BigQuery クエリエディタ")<font color="DarkGray">_BigQuery クエリエディタ_</font>

これを1カラムだけ取得するようにしてみると、159.63MBに。
![img2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2752159/5938f100-4524-801d-0a1f-b4d9ddc40092.png "BigQuery クエリエディタ")<font color="DarkGray">_BigQuery クエリエディタ_</font>

BigQueryはカラム型データ構造が使用されているため、<font color="#ff0000">使用するカラムだけを指定すること</font>がコスト削減につながります。

ちなみに、サブクエリ内でアスタリスク指定によるデータ取得を行っても、<font color="#ff0000">最終的に使用するカラムの分だけにしかコストがかからない</font>所に私は驚きました \:open_mouth:
（普通にアスタリスク指定で取得した分だけコストがかかると思っていました）
![img3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2752159/51bd51fd-8deb-8abc-131f-cdca2814894d.png "BigQuery クエリエディタ")<font color="DarkGray">_BigQuery クエリエディタ_</font>

###### WHERE句

WHERE句を指定しても、テーブル内のレコードをフルスキャンしてWHERE句に一致しているかどうかを確認するため、指定しても処理量は変わらない・もしくは指定したカラム分増える、ということになります。

条件を増やす＝データを絞るんだから処理量減りそう！と思ってしまった人、仲間です:satisfied:
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2752159/3c33fd69-40df-fb6c-cb46-db11db519454.png "BigQuery クエリエディタ")<font color="DarkGray">_BigQuery クエリエディタ_</font>

WHERE句にカラムを1つ増やしただけで、この通り処理量が増えてコスト増になります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2752159/2b190a72-61ae-69d5-1c52-9671037d225d.png "BigQuery クエリエディタ")<font color="DarkGray">_BigQuery クエリエディタ_</font>

###### LIMIT句

FROM句で対象のテーブルを指定し、SELECT句やWHERE句等で取得対象のカラムを指定した時点で処理されるデータ量が決まるため、その後LIMIT句でデータを絞っても処理されるデータ量に影響はなく、コストも変わりません。

先ほどの全カラム取得のSQLにLIMIT句を指定しても、処理されるデータ量に変化はありません。
![img4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2752159/9f794038-abcf-e309-7dae-7fd3e6b3461a.png "BigQuery クエリエディタ")<font color="DarkGray">_BigQuery クエリエディタ_</font>

##### ②キャッシュについて

>エラーが返されたクエリまたはキャッシュから結果が取得されたクエリに対しては料金が発生しません。手続き型言語ジョブの場合、この考慮事項はステートメントごとに提供されます。

BigQueryはクエリを実行する際、キャッシュされた結果があるかまずチェックします。キャッシュされた結果がある場合、その結果を返すので、料金は発生しません。

しかし、このキャッシュに関しては制約があり、たとえば以下のような場合、クエリ結果はキャッシュに保存されないので注意が必要です。

- キャッシュに保存された結果が期限切れになった場合（保存期限は24時間ですが確約ではなくベストエフォート）
- キャッシュ保存後にデータが更新された場合
- 日時関数や実行タイミングによって異なる値を返す関数がクエリで使用されている場合

#### 2-2.定額料金

クエリが処理したバイト数に基づいて料金が請求されるオンデマンド分析に対し、定額料金では購入したスロット数に対して固定料金が請求されます。

##### スロットって何？

スロットとはBigQueryでクエリを実行するために使用される仮想CPUの事で、オンデマンド分析でもスロットを使用しています。

オンデマンド分析の場合はプロジェクトごとに最大2000スロットまで使って実行（超えることもあります）すると記載されています。

>オンデマンド料金では、プロジェクトで最大 2,000 個の同時実行スロットを設定できます。BigQuery スロットは、単一のプロジェクト内のすべてのクエリで共有されます。BigQuery が、クエリを高速化するためにこの上限を超えてバーストする場合があります。

定額料金の場合は、分析に使用する仮想CPUをあらかじめ購入しておくイメージです。

##### 料金

定額料金には3つのプランがあります。
以下は東京（asia-northeast1）の料金例（2022年8月時点）です。（1$=130円で算出）

| プラン     | コミットメント(※) | 購入単位    | 費用参考(100スロット)                                     |
| ---------- | ----------------- | ----------- | --------------------------------------------------------- |
| 月次契約   | 30日              | 100スロット | $2,400 (約312,000円) / 1ヵ月                              |
| 年間契約   | 365日             | 100スロット | $2,040 (約265,200円) / 1ヵ月                              |
| Flex Slots | 60秒              | 100スロット | $4.8 (約624円) / 1時間<p>$3,504 (約455,520円) / 1ヵ月</p> |

※コミットメント</br>
最小使用期間のこと。最小使用期間が過ぎれば、キャンセルしたりプラン変更が可能。

:::note info
［参照］公式ドキュメント
  [定額料金](https://cloud.google.com/bigquery/pricing?hl=ja#flat-rate_pricing)
:::

##### 定額料金の利用検討

- **料金**
  オンデマンド分析と違い、購入するスロット数によってコストが変わるので、オンデマンド分析と定期料金でコストパフォーマンスの比較は単純にはできません。

  上記に記載したように、最低購入単位の100スロットでも月額約26万円以上（年間契約、1$ 130円算出）になりますので、現在のオンデマンド分析での利用状況やこれからの利用予測を行い、本当に定期料金の導入が必要か検討する必要があります。

- **クエリの実行速度**
  クエリの実行速度に関しても、実行するクエリが利用するスロット数に対して定期料金で購入したスロット数が少ない場合、待ち時間が発生し、実行速度が遅くなります。
（参照：[スロット リソース エコノミーにおけるクエリ実行](https://cloud.google.com/bigquery/docs/slots?hl=ja#query_execution_under_slot_resource_economy)）

  現状のスロット利用状況を確認したうえで、コストとのバランスも考えながら購入するスロット数を決める必要があるといえます。

##### スロット見積もりツール

スロット見積もりツールは2022年8月時点でプレビュー状態の機能ですが、オンデマンド分析の場合は過去30日間のスロット利用状況がグラフ化されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2752159/d28612e4-a7f1-d9f0-ebf4-bcd13629fe91.png "BigQuery スロット見積もりツール")<font color="DarkGray">_BigQuery スロット見積もりツール_</font>

また、スロット見積もりツールから、BigQuery Slot Recommenderサービスを利用できます。このサービスは組織内のプロジェクトで過去30日間のスロット利用状況を分析し、オススメのスロット数を提案してくれる機能です。

※ただし、スロットの利用が少ない場合には定額料金への切り替えメリットがないとされ、Recommenderサービスが利用できない旨のメッセージが表示されます。
>Recommendations are unavailable. Slot utilization is too low or too sparse to benefit from switching to a monthly slot reservation.

:::note info
［参照］公式ドキュメント
  [スロットの推奨事項と分析情報の表示](https://cloud.google.com/bigquery/docs/slot-recommender)
:::

上記ドキュメントにて、4つのオプション（コスト重視、バランス重視、パフォーマンス重視、すべての利用法に対応）でスロット数が提案されている画面を確認できます。

上述したように、定額料金の利用を検討する場合、購入する最適なスロット数を決める事が重要になりますが、今まではINFORMATION_SCHEMAを使用してスロット利用状況を分析する作業が必要でした。

BigQuery Slot Recommenderを利用すると、自動的にスロット利用状況を分析・提案をしてくれるのでスロットの見積もりがとても簡単になります。

## まとめ

- BigQueryの料金体系には大きく分けて、ストレージ料金と分析料金がある
- 分析料金にはオンデマンド分析と定額料金がある
- 定額料金を利用する場合、購入するスロット数の見積もりが大切

## （おまけ）分析コストの削減

オンデマンド分析や定額料金に関して解説してきましたが、どちらにも共通していることは、コスト削減において<font color="red">**いかにスキャン範囲を狭めるか**</font>が大切ということです。（※BigQueryは常にフルスキャンなので）

そこで、パーティションを使って処理されるデータ量を減らしましょう。
パーティションとは1つのテーブルを複数のパートに分割する機能です。

クエリ実行時に毎回フルスキャンするのではなく、パーティションによってスキャンする範囲を限定することで、クエリのコスト削減やクエリのパフォーマンス向上にもつながります。

:::note info
［参照］公式ドキュメント
  [分割テーブルの概要](https://cloud.google.com/bigquery/docs/partitioned-tables)
:::

今回は、一般公開データセット内のcovid19_open_dataを使って、パーティション分割テーブルを作成してみました。（時間単位列パーティション・月単位・有効期限180日）

```SQL:パーティション分割テーブル作成サンプル
CREATE OR REPLACE TABLE
  sample_dataset.data_by_month
PARTITION BY
  DATE_TRUNC(date, MONTH) OPTIONS( partition_expiration_days=180 ) AS
SELECT
  date,
  location_key,
  new_confirmed,
  new_deceased
FROM
  `bigquery-public-data.covid19_open_data.covid19_open_data`
WHERE
  country_code = 'JP'
```

:::note info
［参照］公式ドキュメント
  [分割テーブルの作成](https://cloud.google.com/bigquery/docs/creating-partitioned-tables?hl=ja)
:::

INFORMATION_SCHEMA.PARTITIONSで確認すると、以下のようにパーティション分割テーブルが作成されていることを確認できます。

```SQL:INFORMATION_SCHEMA.PARTITIONS
SELECT
  table_schema,
  table_name,
  partition_id,
  total_rows
FROM
  `sample_dataset.INFORMATION_SCHEMA.PARTITIONS`
WHERE
  table_schema="sample_dataset"
  AND table_name="data_by_month"
ORDER BY
  partition_id
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2752159/08d66fc0-6f51-cb4f-d6bc-be81d6874548.png "BigQuery クエリエディタ")<font color="DarkGray">_BigQuery クエリエディタ_</font>

では、実際にパーティション分割テーブルが有効に使用できているか確認してみます。
まずはフルスキャンを実行してみます。処理データ量は約255KBとなりました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2752159/fed98b8a-568a-6d3a-f41c-fc1297451b67.png "BigQuery クエリエディタ")<font color="DarkGray">_BigQuery クエリエディタ_</font>

次に、パーティショニング列を追加して、クエリを実行してみます。
パーティショニング列とはパーティション分割テーブルを作成する際に指定した列のことで、上述のSQLだと「date」列になります。

WHERE句を追加し、パーティショニング列のdate列で2022/6/1～2022/6/30までのデータを取得してみます。処理データ量が約43KBとなり、2022年6月分のパーティション分割テーブルに対してのみスキャンが実行されていることがわかります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2752159/b21bee5b-ac97-bfaf-54b1-14632ed1acfc.png "BigQuery クエリエディタ")<font color="DarkGray">_BigQuery クエリエディタ_</font>

以下のように1日でも翌月のデータを対象にすると、2022年6月分と2022年7月分のパーティション分割テーブルに対してスキャンが実行され、処理データ量が約88KBと増えていることがわかります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2752159/30964b79-123b-974e-b3f3-18d30a198452.png "BigQuery クエリエディタ")<font color="DarkGray">_BigQuery クエリエディタ_</font>

パーティション分割テーブルを適切に作成することで、クエリコストの削減につながるので活用していきましょう。