【実務・中級編】 分離レベル – Cloud Spanner

Cloud Spannerの「最強の分離レベル」をハックする:直列化可能性(Serializable)との付き合い方

Cloud Spannerを触るエンジニアが、最終的に辿り着く「聖域」がある。それがSerializable(直列化可能)だ。

多くのRDBMSでは、性能を稼ぐために「Read Committed」や「Repeatable Read」といった妥協案(分離レベルの引き下げ)を選択し、その代償として複雑な競合解決や幻影読み(Phantom Read)のデバッグに工数を溶かす。だが、Spannerは違う。「分散環境でいかにして全トランザクションを直列化し、かつ超高速に捌くか」という難問に対し、TrueTimeという物理的な回答を用意した。

今日は、Spannerのアーキテクチャの根幹である「シリアライザブル」を、現場で「使いこなす(そして事故らない)」ための極限の知見を授ける。

—

1. なぜ「シリアライザブル」にこだわるのか

Spannerのトランザクションは、外部整合性(External Consistency)を保証する。これは、あるトランザクション $T_1$ が終了した後に開始された $T_2$ が、必ず $T_1$ の結果を参照できることを意味する。

これを支えるのが、TrueTime APIだ。各ノードが持つクロックの誤差(Uncertainty)を考慮し、トランザクションにタイムスタンプを割り当てる。このタイムスタンプこそが、Spannerの「順序付け」の正体だ。

現場の教訓:
「分離レベルを気にしなくていい」というのは、「何も考えなくていい」という意味ではない。「トランザクションの範囲(Scope)を意識しないと、スループットが死ぬ」ということを意味する。

—

2. 堅牢な設計パターン:ReadOnly vs ReadWrite

設計レビューでよく見かける「アンチパターン」は、何でもかんでもReadWriteトランザクションで処理しようとすることだ。

A. ReadOnlyトランザクション(Snapshot Read)

特定のタイムスタンプを指定して読み取る。ロックを獲得しないため、書き込みと競合せず、性能面で最強だ。

— 特定のタイムスタンプでのスナップショット読み取り
— 読み取り専用トランザクションを使えば、ロック待ちによる遅延はゼロになる
SELECT account_balance FROM Accounts WHERE account_id = ‘user_123’;

極限の知見:
「現在時刻」の読み取りが必要ない場合は、`Exact Staleness` を活用せよ。例えば、「15秒前のデータで良い」レポート処理なら、レプリカからの読み込みが可能になり、リーダーノードへの負荷を劇的に低減できる。

B. ReadWriteトランザクション

読み取り、演算、書き込みを行う。競合が発生すれば、Spannerは自動的に「リトライ」を行う。

現場の教訓:
ここでの最大の敵は「トランザクションの肥大化」だ。

  • 悪い設計: ReadWriteトランザクション内で、外部APIコールや重い計算を行う。
  • 良い設計: 「Read(必要なデータ取得) -> 計算 -> Write(更新のみ)」という構造を徹底し、トランザクションの生存時間を極限まで短くする。

—

3. パフォーマンスと競合のリアル

Spannerのシリアライザブルは、楽観的並行性制御(OCC)に似た挙動を見せる。競合が発生すると、トランザクションはアボートされる。

競合を避けるための「ホットスポット回避」

特定のキー(例:単一のカウンタ行)に書き込みが集中すると、ロックの競合でスループットは天井に張り付く。

  • シャード化: カウンタを100個の行に分散させ、合計を後から集計する。
  • キーの設計: 順序性のあるキー(単調増加するIDなど)は、物理的なスプリットの偏りを生む。UUID v4などを採用し、書き込み負荷をクラスタ全体に拡散させよ。

—

4. チーフアーキテクトからの「最後のアドバイス」

現場のシステムで「トランザクションが多発する」というアラートを見たら、まずコードを確認してほしい。

1. クライアント側でリトライを適切に実装しているか?
Spannerは競合時に `Aborted` エラーを返す。これは「失敗」ではなく「再試行が必要な正常なレスポンス」だ。Googleのクライアントライブラリはこれを自動化しているが、複雑なアプリケーションロジックを挟むと、この恩恵を受けられなくなることがある。
2. 不必要な読み取りを行っていないか?
ReadWriteトランザクション内での読み取りはロックを伴う可能性がある。不要なSELECT文をトランザクションの先頭に置いていないか、再評価せよ。

結論

Spannerのシリアライザブルは、魔法ではない。強力なエンジニアリングの結晶だ。
「分離レベルがシリアライザブルだからバグらない」と安堵するのではなく、「シリアライザブルという制約の中で、いかにトランザクションを短く、いかにホットスポットを回避するか」に知恵を絞ること。それが、Spannerを制する唯一の道だ。

コードを書くとき、自問自答してほしい。「このトランザクションの生存時間は、ミリ秒単位で削減できるか?」と。その執念こそが、大規模分散システムを支えるエンジニアの矜持だ。

コメント

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