【Cloud Spanner】ポイントインタイムリカバリ(PITR)の極意:データ破損から秒速で生還するための実践アーキテクチャ
こんにちは。チーフアーキテクトの私だ。
今日のコードレビュー、あるいは基本設計のレビューで、こんな質問を受けたとしよう。
> 「ねえ、SpannerのPITRってデフォルトで有効になってるし、バックアップ取っとけば安心ですよね? 誤って本番テーブルを`TRUNCATE`しちゃっても、過去の任意の秒に巻き戻せるんですよね?」
この問いに対し、君は即座に、かつロジカルにその裏にあるリスクとコスト構造を説明できるか?「はい、できます」と答えた瞬間、そのエンジニアの担当するシステムは、障害発生時に泥沼のリカバリ劇を演じることになる。
Cloud Spannerのポイントインタイムリカバリ(PITR:Point-in-Time Recovery)は、神のような機能だが、「仕組みと物理制約を理解した者」にしか微笑まない。今日は、このPITRを単なるお守りではなく、実戦で確実に機能させるための「極限の知見」を伝授しよう。
—
1. SpannerのPITR:その下回り(内部挙動)はどうなっているのか?
一般的なRDBのPITR(WALのロールフォワードなど)を想像していると、Spannerのそれを見たときに頭がバグる。
Spannerのデータは、内部でTrueTimeという極めて高精度な時刻同期基盤に裏打ちされたバージョン管理ストレージ(LSMツリーベース)として保持されている。つまり、Spannerにとって、過去のデータにアクセスすることは「特別な復旧作業」ではなく、「過去のタイムスタンプを指定した通常の読み取り(Stale Read)」の延長線上にすぎない。
- 保持期間(Retention Period): 最長7日間(168時間)。デフォルトは通常7日。
- ストレージコスト: PITRを有効にすると、過去のバージョンを保持するためのストレージ容量が増加する。当然、更新頻度(Churn Rate)が高いテーブルほど、PITRの保持にかかるストレージコストは膨らむ。
「バックアップ」との決定的な違い
よく混同されるが、明示的な「バックアップ(Cloud Spanner Backup)」と「PITR」は別物だ。
| 特性 | バックアップ (Backup) | PITR (Point-in-Time Recovery) |
| :— | :— | :— |
| 目的 | 長期保存、ディザスタリカバリ、監査用 | ヒューマンエラー、バグによるデータ破損の迅速な復旧 |
| 最大期間 | 無期限(GCSに保存) | 最長7日 |
| リストア先 | 新規インスタンスとして復元 | 既存インスタンス内(または別インスタンス)の別テーブルへ復元可能 |
| 復元粒度 | バックアップ取得時点 | マイクロ秒単位(TrueTimeの範囲内) |
—
2. 実務で直面する「PITRリカバリ」の罠とパフォーマンス影響
「よし、昨日の15:00のデータに戻そう」
――チーフアーキテクトとして、私は声を大にして言いたい。本番環境で「インプレース(同一インスタンス内)のテーブル上書き」によるPITR復元を軽々しくやってはならない。
罠1:リストア時のI/Oバーストとレイテンシの悪化
PITRからの復元(Restore)を実行すると、裏側でデータのスナップショットから新しいテーブル(あるいはインスタンス)へのデータ構築が走る。
大規模なデータベース(数TB〜数十TB)において、これを安易に行うと、ストレージノードのI/Oリソースが枯渇し、本番のトランザクションレイテンシが跳ね上がる。
特に、リストア先のデータベースで大量のインデックス再構築やデータコピーが発生する場合、CPU使用率とストレージの読込/書込スループットが急上昇する。
罠2:「誤って消したデータだけ」を戻す難しさ
PITRは「指定したタイムスタンプのデータベース/テーブルの状態」を丸ごと別領域に復元する。
「特定のユーザーが誤って更新した1行、あるいは1バッチがやらかした数万行」だけを綺麗に現行テーブルにマージ(UPSERT)するには、復元したテーブルと現行テーブルをJOINして差分を抽出するカスタムクエリとアプリケーションロジックが自前で必要になる。Spannerが自動で「そこだけ戻してくれる」わけではない。
—
3. 堅牢な設計パターン:プロはどう運用するか?
では、我々プロのエンジニアはこの強力な機能とどう向き合うべきか。設計のベストプラクティスを提示しよう。
パターンA:保持期間のコスト最適化
すべてのデータベースで一律に7日間を維持する必要はない。
開発・ステージング環境ではPITRを無効化するか、保持期間を最短(例:1〜2日)にする。本番環境であっても、不変性(Immutability)が高いログ系テーブルと、高頻度で更新されるトランザクション系テーブルでデータベースを分離している場合、データベース単位で適切な保持期間を設定する。
パターンB:GCLI / Terraform によるPITR設定のコード化
インフラの構成管理はすべてIaC(Terraformなど)で行うべきだ。PITRの保持期間(`version_retention_period`)もコードで厳格に管理する。
TerraformによるSpannerインスタンスおよびデータベースの定義例
resource “google_spanner_database” “prod_db” {
instance = google_spanner_instance.prod_instance.name
name = “core-transaction-db”
# PITRの保持期間を明示的に7日間に設定(デフォルトだがコード化が鉄則)
version_retention_period = “7d”
# 意図しない削除を防ぐライフサイクル
lifecycle {
prevent_destroy = true
}
}
パターンC:障害復旧(DR)ランブックの自動化と検証
「バックアップはある、PITRもある。だが、リストアの手順を誰もテストしたことがない」――これほど恐ろしいホラーはない。
四半期に一度は、ステージング環境または隔離されたテスト環境において、以下のリカバリ・ドリル(訓練)を義務付けろ。
1. 障害発生の模擬: テストデータを意図的に破壊する。
2. PITR復元: gcloudコマンドを用いて、特定のタイムスタンプを指定して別テーブルへ復元する。
3. 差分マージ: 復元データから正当なデータを抽出し、現行系へリストアするスクリプトを走らせる。
4. 所要時間の計測: RTO(目標復旧時間)内に収まるか検証する。
リストア実行のコマンド例
過去の特定のタイムスタンプ(RFC 3339形式)を指定して別データベースとして復元
gcloud spanner databases restore restored-db-from-pitr \
–instance=prod-instance \
–source-instance=prod-instance \
–source-database=core-transaction-db \
–backup-time=2023-10-27T10:00:00Z
(※注: ガードレールとして、復元先は必ず別名データベース、あるいは検証用の別インスタンスを切るべきである。)
—
4. チーフアーキテクトからの最終提言
Cloud SpannerのPITRは、魔法の杖ではない。しかし、「正しい設計(インフラ分離、コスト管理)」と「事前の訓練(リカバリ手順の自動化)」が噛み合った瞬間、どんなヒューマンエラーやバグをも笑い飛ばせる最強の盾に変わる。
コードレビューで同僚が「PITRがあるから大丈夫です」と言ってきたらこう返すんだ。
> 「その保持期間のコスト試算は済んでいるか? そして、そのPITRから実際にデータを救い出すスクリプトの結合テストは、今週の金曜までに終わるのか?」
この問いに淀みなく答えられるチームだけが、真にCloud Spannerのポテンシャルを解放し、高可用な分散システムを統べる資格を持つ。
さあ、設計書を開き直そうか。君たちのシステムは、本当に秒速で生還できるか?
コメント