BigQueryのストレージ課金モデル(論理バイト/物理バイト)を比較する

# BigQueryデータセットのストレージ消費単位
論理バイト単位で課金されていたのですが、2023/07/05に料金改定が行われてデータ圧縮後の物理バイトを課金の単位にすることもできるようになりました。物理バイト単位に変更することで安くできることが多いようなので調べてみました。
論理・物理というのはテーブル詳細画面にも記載されていて、論理がデータをそのまま保存した場合の容量で、物理は圧縮して実際にかかっている容量のことのようです。
![スクリーンショット 2023-06-03 18.45.56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/534203/7badb049-5baf-359f-8945-d7f9e7cd314a.png)


# 料金について
※USリージョンの場合
| | 論理バイト | 物理バイト |
| ---- | ---- | ---- |
| アクティブ(USD/GB) | 0.02 | 0.04 |
| 長期(USD/GB)   | 0.01 | 0.02 |

一見、物理バイトのほうが高く見えるのですが圧縮率が1/10になることもあるようで、その場合は物理バイトで課金したほうが安くなります。
物理バイトの場合はタイムトラベル用のストレージも含めて計算する必要があります。（逆に論理バイトはタイムトラベル用ストレージは考慮しなくて良いです)
また、90日間変更がないと自動的に長期のストレージとして扱われます。


# クエリでどちらを選べば良いか調べよう
どちらがいいかはデータの内容によるのですが、便利なINFORMATION_SCHEMAを使った以下のクエリを実行してどちらが安いかを調べることができます。（[公式ドキュメントの例](https://cloud.google.com/bigquery/docs/information-schema-table-storage?hl=ja#examples)を元に作成しました。）
`active_logical_gib_price`などの料金、`usd_rate`(為替レート)、`region-us`(リージョン)の部分は適切な値に変更してください。
```SQL
DECLARE
  active_logical_gib_price FLOAT64 DEFAULT 0.02;
DECLARE
  long_term_logical_gib_price FLOAT64 DEFAULT 0.01;
DECLARE
  active_physical_gib_price FLOAT64 DEFAULT 0.04;
DECLARE
  long_term_physical_gib_price FLOAT64 DEFAULT 0.02;
DECLARE
  usd_rate FLOAT64 DEFAULT 138.;

WITH
storage_sizes AS (
  SELECT
    table_schema AS dataset_name,
    SUM(active_logical_bytes) / POWER(1024, 3) AS active_logical_gib,
    SUM(long_term_logical_bytes) / POWER(1024, 3) AS long_term_logical_gib,
    SUM(active_physical_bytes) / POWER(1024, 3) AS active_physical_gib,
    SUM(long_term_physical_bytes) / POWER(1024, 3) AS long_term_physical_gib,
  FROM
    `region-us`.INFORMATION_SCHEMA.TABLE_STORAGE_BY_PROJECT
  WHERE
    total_logical_bytes > 0
    AND total_physical_bytes > 0
  GROUP BY
    table_schema
),
cost AS (
  SELECT
    *,
    active_logical_gib * active_logical_gib_price + long_term_logical_gib * long_term_logical_gib_price AS logical_cost_usd,
    active_physical_gib * active_physical_gib_price + long_term_physical_gib * long_term_physical_gib_price AS physical_cost_usd,
  FROM
    storage_sizes
),
cost_in_yen AS (
  SELECT
    *,
    CAST(logical_cost_usd * usd_rate AS INT64) AS logical_cost_yen,
    CAST(physical_cost_usd * usd_rate AS INT64) AS physical_cost_yen,
    CAST((physical_cost_usd - logical_cost_usd) * usd_rate AS INT64) AS diff_cost_yen_when_change_physical,
  FROM
    cost
)
SELECT
  dataset_name,
  logical_cost_yen AS l_cost,
  physical_cost_yen AS p_cost,
  diff_cost_yen_when_change_physical AS diff_cost,
FROM
cost_in_yen
ORDER BY diff_cost_yen_when_change_physical
```

各数値の意味は以下の通りで、単位は全て日本円です。
- l_cost: 論理バイトで計算した場合の金額
- p_cost: 物理バイトで計算した場合の金額
- diff_cost: 論理バイト -> 物理バイト と変更した場合の金額の差


## 出力例
|dataset_name| l_cost|p_cost|diff_cost|
|----|----:|----:|----:|
|web_log|20000|1800|-18200|
|keiba|8000|2000|-6000|
|web_mart|15|20|5|
|web_analysis|28000|30000|2000|

diff_costの列を見てください。
上の例では、web_log, keibaのデータセットでは物理バイトで課金したほうが安くなります。
web_analysisデータセットがそうなんですが、ほとんどの列のカーディナリティが高くて圧縮率が低くならない場合や、毎日集計しているようなテーブルはタイムトラベル用ストレージが高くなり論理バイトで課金したほうが安くなるようです。
web_martのように日時や月次で集計したテーブルは容量が小さいのでどちらでも安いことが多いです。

# まとめ
ストレージ料金はGCSの料金と同じで無料枠もあって安いよなーとも思うんですが、ビッグデータを扱うと結構な金額になってしまいます。それが毎月課金となるのでお得な課金モデルを選択してコストを削減しましょう。


正確な情報はBigQueryの公式ドキュメントを参照してください。
https://cloud.google.com/bigquery/docs/datasets-intro?hl=ja#dataset_storage_billing_models
https://cloud.google.com/bigquery/docs/updating-datasets?hl=ja#update_storage_billing_models