🌊

Apache Iceberg 用の BigQuery テーブルのクラスタリングについて

に公開

はじめに

Google Next 2025 に参加してきました。Google Next に参加してわかった注意事項などは同僚が綺麗にまとめてくれたので、自分は最初からセッションで得た知見について確認していきたいと思います。

今回の Next では BigQuery 関連のセッションでは Iceberg 対応に関連した話が多く出ていました。そこで今回は Apache Iceberg 用の BigQuery テーブルについて調査してみることにしました。

調査のモチベーションは以下です:

  • 実際に利用に際し、コスト最適化の方法について把握したい。
  • 案件においてはクエリコストの最適化は読み込み元ファイルを日付ごとにフォルダを作成して保存し、それらを BigQuery のそれぞれのパーティションに取り込むことでクエリコストの最適化を行うことが多い。
  • Iceberg テーブルはパーティショニングが利用できない。

前提

すでに、Apache Iceberg 用の BigQuery テーブルの作成については公式ドキュメントやブログなどで多く紹介されているため、今回は細かい作成手順などについては割愛しております。

動作確認

ダミーファイルの準備

読み込み元ファイルは以下のような形式で作成しました。

 head -n 5 2025-04-01.csv
id,name,number,description,create_date
1,user001,1,rdykitdkafmqdxswkvntoiwqcrsulm,2025-04-01T00:00:00Z
2,user002,2,vumokexpqzwuiliosystwjpgvuhjys,2025-04-01T00:00:00Z
3,user003,3,pvituewrtvdinmcofhvmrjmswlhixj,2025-04-01T00:00:00Z
4,user004,4,wsutjncamavoyddipmmqqdmhajbazd,2025-04-01T00:00:00Z

ファイルが小さすぎるとロードした際にファイルが分割されない可能性があるため、ファイルサイズがある程度の大きさになるように生成しました。

$ ls -lh 2025-04-01.csv
-rw-r--r--@ 1 nobuhiro.ohashi  staff    74M  4 23 07:15 2025-04-01.csv

また、create_date のカラムでクラスタリングすることを想定しているため、2025-03-01T00:00:00Z となっているバージョンも作成しました。

$ head -n 5 2025-03-01.csv
id,name,number,description,create_date
1,user001,1,cpqslzgzsirlnyqsukeajbvefjyaaa,2025-03-01T00:00:00Z
2,user002,2,qszhcggzpohuydvjguykerrjddzoot,2025-03-01T00:00:00Z
3,user003,3,qglylgmgwoeneyrnquhfobqaettmzg,2025-03-01T00:00:00Z
4,user004,4,sobnwbuqveomcjteeflpmfuesoezsl,2025-03-01T00:00:00Z
$ ls -lh 2025-03-01.csv
-rw-r--r--@ 1 nobuhiro.ohashi  staff    74M  4 23 07:15 2025-03-01.csv

テーブル作成

以下の SQL を実行して Iceberg テーブルを作成します。

CREATE TABLE `<データセット名>.<テーブル名>` (
    id INT64,
    name STRING,
    number INT64,
    description STRING,
    create_date TIMESTAMP
)
CLUSTER BY create_date         -- タイムスタンプでクラスタリング
WITH CONNECTION `projects/<プロジェクト名>/locations/us/connections/<コネクション名>`
OPTIONS (
    file_format = 'PARQUET',
    table_format = 'ICEBERG',
    storage_uri = 'gs://<バケット名>/<テーブルデータのパス>/'
);

次にダミーデータをロードします。

LOAD DATA INTO `<データセット名>.<テーブル名>`
FROM FILES (
  format = 'CSV',
  uris = ['gs://<バケット名>/<ダミーデータのパス>/*.csv'],
  field_delimiter =',',
  skip_leading_rows = 1
);

ロードした結果、ファイルは数十 MB ごとに分割されていることがわかります。

ファイルサイズ

クエリの読み込み容量について

まずは、Iceberg テーブルのデータを読み込んでみます。

SELECT 
  * 
FROM 
  `<データセット名>.<テーブル名>`

全量読み込み

次にクラスタリングキーを絞り込んだ上でデータを読み込んでみます。

SELECT 
  * 
FROM 
  `<データセット名>.<テーブル名>`
WHERE DATE(create_date) = '2025-03-01'

読み込み条件で絞りこみ

想定通り、読み込み容量が削減されていました。

また、Parquet はカラムナーフォーマットのため、特定カラムだけを読み込めるようになっています。
そこで、id カラムだけを取得してみます。

SELECT 
  id
FROM 
  `<データセット名>.<テーブル名>`
WHERE DATE(create_date) = '2025-03-01'

さらにカラムも絞りこみ

当たり前ですがデータの読み込み容量は削減されます。

データの追加

追加のダミーファイルを作成してロードしてみます。

$ head -n 5 2025-02-01.csv
id,name,number,description,create_date
1,user001,1,zphovaoxmhltssdvjkpneaxdnddlwk,2025-02-01T00:00:00Z
2,user002,2,rmnkygbjvuqvplberxbenjjnndkehp,2025-02-01T00:00:00Z
3,user003,3,sydqsityfrkcfnuzleugfvfovfxfbl,2025-02-01T00:00:00Z
4,user004,4,ktbmegxcghfapnzbbhrjwrwegnwiyi,2025-02-01T00:00:00Z
$ ls -lh 2025-02-01.csv
-rw-r--r--@ 1 nobuhiro.ohashi  staff    74M  4 23 07:56 2025-02-01.csv
LOAD DATA INTO `test_iceberg.test`
FROM FILES (
  format = 'CSV',
  uris = ['gs://<バケット名>/<ダミーデータのパス>/2025-02-01.csv'],
  field_delimiter =',',
  skip_leading_rows = 1
);

ロードした結果、バケットには追加のファイルが作成されました。

追加されたファイル

結論

Iceberg テーブルでもクラスタリングを使うことで日次のデータを出力日ごとに絞り込むことができます。
一方で、作成されたファイルがバケットにフラットに配置されてしまうため、特定の日付のファイルをダウンロードして手元の端末で操作したいなどは難しいため注意が必要です。
なお、Hive フォーマットのように物理的にデータを隔離しないことで、データ分割に伴うパフォーマンスを BigQuery 側でコントロールできたり、クエリを実行する際にデータの配置状況を意識する必要がなくなるため上記のような技術選択が実施されたのかなと推察されます。
(Timestamp で Hive フォーマットでのデータ分離はできないため、日付まで丸めたデータでパーティショニングし、そちらで絞り込んだ上で追加で Timestamp 型のカラムでも追加で絞り込むといった使い方をしないで済むように。)

しかし、GCS 上のファイルを意識しないのであれば通常の利用方法の範囲であれば、ほとんどネイティブテーブルとの違いを意識しないでも利用していけそうです。

※ データ分析、データ基盤構築、及び AI 活用に関するご相談は、以下よりお気軽にお問い合わせください。
お問い合わせフォーム

株式会社 MBK デジタル

Discussion