【テクニカル・上級編】 サブクエリとCTE – Cloud Spanner

Cloud Spannerの深淵:サブクエリとCTEが実行計画にもたらす「コストの真実」

Cloud Spannerは、分散トランザクションの金字塔だ。しかし、多くのエンジニアが「SQLが書けるから」という理由だけで、この分散データベースの心臓部をブラックボックスとして扱っている。

今日は、サブクエリとCTE(共通テーブル式)という、一見ありふれたSQL構文が、Spannerの分散クエリ実行エンジン(Distributed Query Processor)において、いかにしてメモリ空間と通信オーバーヘッドを激しく揺さぶるのか、その「極限の知見」を紐解く。

—

1. サブクエリの「隠れたコスト」:非同期実行とメモリの罠

Spannerにおいて、サブクエリは単なる「入れ子」ではない。特に、相関サブクエリ(Correlated Subquery)を不用意に書くことは、分散ノード間での「ネステッドループ結合」の嵐を招く行為に他ならない。

実行計画への影響

Spannerのオプティマイザは、サブクエリを可能な限り`Apply`演算子(または`Correlated Join`)として展開しようとする。これが大規模データセットに対して実行されると、外側のクエリが1行処理するたびに、内側のサブクエリがノードを跨いで再評価される。

  • 物理的な挙動: ノード間のRPC呼び出しがO(N)で増大する。
  • メモリの挙動: 中間結果セット(Intermediate Result Set)がメモリに展開されるが、Spannerのクエリ実行エンジンは「全結果をメモリに保持すること」を嫌う(メモリリーク防止のクォータ制限があるため)。

教訓: 大規模なサブクエリを記述する場合、`EXPLAIN ANALYZE`で`Distributed Union`がどこで発生しているかを見よ。サブクエリが各スプリット(Split)に対して個別に投げられているのなら、それは設計の敗北だ。

—

2. CTEの再評価:最適化の「魔法」と「呪い」

CTE(`WITH`句)は可読性のためにあるのではない。真のアーキテクトは、CTEを「クエリ内のスコープ付き一時テーブル」として捉える。

SpannerにおけるCTEの真実

多くのエンジニアは、CTEを書けば結果がキャッシュされると誤解している。しかし、Spannerの現在の実行エンジンにおいて、CTEは単なる「インライン展開されるビュー」として扱われることが多い。

  • インライン展開の代償: 同一のCTEを複数回参照する場合、Spannerはそれを複数回再実行する可能性がある。これは、計算コストの高い集計処理をCTEに置いた瞬間に、クエリのレイテンシが倍増することを意味する。
  • マテリアライゼーションの制御: コンパイラがクエリグラフを最適化する際、CTEが単なる一時的な中間ステップなのか、それとも「全ノードで再利用すべき計算結果」なのかを適切にハンドリングさせる必要がある。

— 不適切なCTEの例:再計算が発生し、分散ノードで無駄なCPUを消費する
WITH HeavyAggregation AS (
SELECT user_id, SUM(amount) as total FROM Transactions GROUP BY 1
)
SELECT a.user_id, b.info
FROM HeavyAggregation a
JOIN Users b ON a.user_id = b.id
WHERE a.total > 1000;

このクエリにおいて、`HeavyAggregation`の結果がスプリットを跨いでメモリに展開される際、そのデータが「再利用可能か」あるいは「再計算が必要か」をオプティマイザに伝えるためのヒント(あるいはクエリの構造化)が、パフォーマンスの分水嶺となる。

—

3. チーフアーキテクトからの「極限の最適化」戦略

もし君が、数億行のデータに対してミリ秒単位のレスポンスを求めるのであれば、以下の原則を胸に刻め。

A. 相関サブクエリは「JOIN」へと昇華させろ

相関サブクエリをJOINに書き換えることは、オプティマイザに「ハッシュ結合」や「マージ結合」を選択させる余地を与える。分散環境において、ハッシュ結合はノード間でのデータ交換コストを最適化する最良の手段だ。

B. CTEの「副作用」を見極めろ

CTEが複数箇所で参照される場合、それが本当に「計算結果の使い回し」になっているか、`EXPLAIN`を確認せよ。もし再計算が行われているなら、あえてサブクエリを分解するか、あるいは中間結果を一時的なテーブルに書き出す(あるいはアプリケーション層でマッピングする)という「分散DBの定石」に立ち返る勇気を持つことだ。

C. 実行計画の「ノード内コスト」と「ネットワークコスト」を分離して読め

Spannerの実行計画で最も注目すべきは、データがどこで再分配(Repartitioning)されているかだ。サブクエリやCTEが複雑であればあるほど、データはネットワークを駆け巡る。ネットワークは、CPUよりも100倍遅いという現実を忘れてはならない。

—

結びに代えて

Cloud Spannerは魔法の箱ではない。それは高度に抽象化された分散システムであり、我々エンジニアが投げるSQLの「論理的な美しさ」を、物理的な「分散リソースの効率」へと翻訳するエンジンだ。

サブクエリもCTEも、ただのツールに過ぎない。重要なのは、その構文がSpannerの分散実行エンジンの中でどのような物理プランに変換され、どのノードのメモリをどれだけ消費しているかを「透視」できるか否かだ。

それができる者だけが、Spannerの真の力を引き出し、限界を突破できる。君のクエリが、今日よりも明日、より洗練された分散実行計画を生み出すことを期待している。

コメント

タイトルとURLをコピーしました