Spannerの「一貫性」をハックせよ:読み取り専用トランザクションの真実と設計の最適解
Cloud Spannerを触り始めて最初に直面する壁、それは「RDBの常識が通じない」という感覚だ。特にトランザクションの設計において、Read-Write(RW)とReadOnly(RO)を適当に使い分けているなら、君のシステムはSpannerの真価を半分も引き出せていない。
今日は、Spannerのアーキテクチャの根幹である「TrueTime」と「分散トランザクション」の仕組みをベースに、エンジニアが実務で踏むべき「最適解」を叩き込む。
—
1. RWトランザクション:コストを支払って「最強の整合性」を買う
RWトランザクションは、Spannerの真骨頂である「外部整合性(External Consistency)」を保証するためのものだ。
なぜRWは重いのか?
RWトランザクションを開始すると、SpannerはPaxosグループ全体での合意形成を行う。書き込み時にはロックを獲得し、トランザクション完了までそのリソースを占有する。ここで重要なのは「競合」だ。頻繁に更新される行に対してRWを投げれば、レイテンシは跳ね上がり、スループットは死ぬ。
設計の鉄則
- 「RWは最小単位で」: トランザクションの中で、外部APIを叩いたり、重い計算を挟むな。ロックの保持時間を1ミリ秒でも短くしろ。
- 「ホットスポットを回避せよ」: インクリメントカウンターのような設計はSpannerでは最悪だ。物理的なシャードに負荷が集中する。キーの設計で負荷を分散させるのが、プロの仕事だ。
—
2. ROトランザクション:Spannerの「チート」を使いこなす
読み取り専用トランザクション(RO)は、Spannerが世界最強の分散DBたる所以だ。ここを理解せずにSpannerを語るな。
ROの秘密:スナップショット読み取り
ROトランザクションは、指定したタイムスタンプ(あるいは現在時刻)の「スナップショット」を読み取る。これにより、ロックを取得する必要が一切ない。 つまり、書き込み処理と読み取り処理が互いにブロックし合わないのだ。
実践:厳密な整合性が不要な場合の「Staleness」活用
もし、数秒程度の遅延が許容できるデータ(ダッシュボードや分析用データなど)であれば、`Exact Staleness`を指定しろ。
// 5秒前のスナップショットを読み取る(ロック不要で高速)
TimestampBound bound = TimestampBound.ofExactStaleness(5, TimeUnit.SECONDS);
try (ResultSet resultSet = dbClient.singleUseReadOnlyTransaction(bound)
.executeQuery(Statement.of(“SELECT FROM Inventory”))) {
// 読み取り専用のクエリ実行。競合を気にせず爆速で処理される
while (resultSet.next()) {
// …
}
}
この設定により、Spannerはローカルレプリカからデータを読み取ることが可能になり、レイテンシは劇的に改善する。
—
3. パフォーマンスと設計の「極限の勘所」
実務の現場で私がレビュー時に必ず指摘するポイントを列挙する。これを守るだけで、君のサービスの品質は一段上がる。
A. 「Read-OnlyはSingle-Useで」
読み取りのみの処理であれば、`ReadOnlyTransaction`を個別に生成するのではなく、`SingleUseReadOnlyTransaction`を使え。トランザクションの開始・終了のオーバーヘッドを削減できる。
B. 「RWからROへのフォールバックを考えるな」
よくあるミスが、「RWの中でクエリを投げて、必要なら更新する」というパターンだ。読み取りは極力ROで行い、最後に必要なデータだけをRWで更新する。「読み取りと更新の分離」こそが、高負荷時でもシステムを落とさないための防波堤だ。
C. 「強い整合性が本当に必要か?」
全てのクエリをデフォルトの整合性で実行するのは、ただの無駄遣いだ。
- 決済処理: 強い整合性(デフォルト)。
- プロフィール表示: `Exact Staleness` で数秒の遅延を許容し、スケーラビリティを確保。
この「妥協点」をビジネスサイドと合意形成できるエンジニアこそが、真のアーキテクトだ。
—
最後に:エンジニアへの提言
Spannerは、君たちがこれまで培ってきた「RDBのチューニング技術」を一度忘れさせることを要求してくる。
- RW: ロックと競合に神経を尖らせ、最小限のスコープで完結させる。
- RO: TrueTimeの恩恵を最大限に活用し、ロックフリーな読み取りを追求する。
この2つを使い分ける感覚が手に馴染めば、君はもうSpannerを恐れる必要はない。次は、どのクエリがインデックスを適切に使えていないか、`Query Plan`を読み解く段階へ進もう。
設計に迷ったら、常に「このクエリはPaxosグループ間で同期を取る必要があるのか?」と自分に問いかけろ。その自問自答の中に、答えはある。
コメント