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