Cloud Spannerの実行計画(Execution Plan)を「ハック」せよ:パフォーマンスの深淵へ
いいか、Spannerを「ただのマネージドなRDBMS」だと思っているなら、今すぐその考えを捨てろ。
Spannerは、分散システムという名の荒野を、強整合性とグローバルなスケーラビリティという魔法で切り拓くモンスターだ。そのエンジンの心臓部であるクエリ・オプティマイザが何を考え、なぜそのような実行計画を導き出したのか。これを読めないエンジニアは、たとえコードが動いていても、いつか巨大な地雷を踏むことになる。
今日は、Spannerの実行計画(Query Execution Plan)を解剖し、実戦で「勝てる」クエリを書くための極意を伝授する。
—
1. 実行計画は「物語」である
実行計画を眺めるとき、上から下へ視線を流すのはアマチュアだ。下から(あるいはリーフノードから)上へと、データの流れを脳内でシミュレートしろ。
Spannerの実行計画には、主に3つのフェーズが見えるはずだ。
- Scan (Leaf): データを物理ストレージ(SSTable)から拾い上げる。
- Filter/Join/Aggregate (Intermediate): データを加工し、結合し、縮約する。
- Distributed/Parallel: 複数のスプリットをまたいで並列処理する。
特に注目すべきは、「Distributed」の文字があるかどうかだ。これが付いている演算子は、ネットワークを跨いでデータが飛び交っていることを意味する。分散システムの最大のコストは「計算」ではなく「通信」だ。実行計画に「Distributed Union」が頻発しているなら、お前のスキーマ設計かクエリは、既に死にかけている。
—
2. コスト見積もりを「疑え」
実行計画を見ると、`Estimated Cost` という値があるだろう。だが、これを絶対視するな。オプティマイザは統計情報(Statistics)に基づいて動くが、統計は常に「過去の残像」だ。
実戦的チェックリスト:
1. Distributed Unionの多発:
インデックスが効いていない証拠だ。`Force Index`を検討するか、インデックスのキー構成を再考しろ。
2. Full Table Scan:
クエリに `WHERE` 句が足りないか、あるいはデータ型がインデックスの型と不一致を起こして暗黙の型変換(Implicit Casting)が発生していないか?
3. Cross Apply / Nested Loop Join:
結合相手の行数が少ない場合は強力だが、数万行を超えると悲劇を生む。`HASH JOIN` への誘導、あるいは結合順序の工夫が必要だ。
—
3. 具体的な「読み」の例
以下のクエリと実行計画の一部を見てみろ。
— ユーザーIDと注文日時でフィルタリングする典型的なクエリ
SELECT u.Name, o.OrderID
FROM Users AS u
JOIN Orders AS o ON u.UserID = o.UserID
WHERE u.UserID = ‘user_123’ AND o.OrderDate > ‘2023-01-01’;
実行計画の核心部分:
+ Distributed Union
+ Local:
+ Join (Inner)
+ Index Scan (UsersByUserID) <-- 効率的
+ Index Scan (OrdersByUserID) <-- 効率的
もし、ここが `Table Scan` ではなく `Index Scan` になっているなら、設計は合格だ。だが、もし `OrdersByUserID` が巨大で、結合後にさらにフィルタリングしているなら、それは「結合後にフィルタ」しているのか、「インデックスで絞り込んでから結合」しているのかを厳密に見極めろ。
「結合後のフィルタ」は悪だ。 結合の前にインデックスで可能な限りデータを間引く(Push Downする)。これがSpannerのパフォーマンスを決定づける鉄則だ。
—
4. チーフアーキテクトからの「禁忌」
最後に、現場でよく見る「やってはいけないこと」を2つ置いておく。
1. 複雑な `OR` を使うな:
`OR` は多くの場合、オプティマイザを混乱させ、`Full Table Scan` に逃げ込む原因になる。`UNION ALL` でクエリを分割する方が、実行計画は遥かに予測可能になる。
2. 実行計画の「変化」を恐れるな:
テーブルのデータ量が100倍になれば、オプティマイザは `Nested Loop` から `Hash Join` に戦略を変える。これは正常な進化だ。だが、「統計情報の更新頻度」と「クエリの重さ」には常に目を光らせろ。`Table Statistics` が古いままだと、オプティマイザは「昨日までの常識」でクエリを計画し、爆死する。
—
終わりに
Cloud Spannerの実行計画を読み解く力とは、すなわち「Spannerがどうデータを保持し、どうネットワークを使い、どう計算資源を割り当てているか」という物理的な制約を理解する力だ。
画面上のGUIに表示される実行計画は、単なるツリー図じゃない。それはお前の書いたクエリに対する、Spannerからの「挑戦状」だ。
「なぜ、ここはDistributed Unionなのか?」「なぜ、ここはHash Joinを選んだのか?」
そう自問自答し続けた先にしか、真の最適化はない。さあ、コンソールを開いて、お前のクエリの実行計画を冷徹に分析してこい。健闘を祈る。
コメント