【実務・中級編】 バックアップ・リストアアーキテクチャ – Cloud Spanner

Cloud Spannerバックアップ&リストアの深層:Colossusの魔術と、真のゼロ・ダウンタイム運用

こんにちは。テクニカルリードの私だ。
今日のコードレビュー、あるいはアーキテクチャ設計レビューで、君たちはCloud Spannerのバックアップ戦略についてどう語るだろうか?

「とりあえず夜間バッチでフルバックアップを取って、Cloud Storageに吐き出しておけば安心です」
もし若手エンジニアがそう言ったとしたら、君はこう聞き返すべきだ。「おいおい、ここは21世紀のグローバル分散データベースだぞ。TB級のデータをストレージにダンプするのに何時間かける気だ? リストア時のRTO(目標復旧時間)はどう担保する?」と。

Cloud Spannerのバックアップとリストアは、従来のRDB(MySQLやPostgreSQL)のそれとは根本的に異なる。ディスクI/Oを圧迫せず、数テラバイトのデータであっても一瞬でスナップショットを生成し、任意のミリ秒単位の過去へタイムトラベルする。

今回は、Googleの分散ファイルシステム「Colossus」の深部から、実務で絶対に外してはならない堅牢な設計パターンまで、私の知見のすべてを叩き込む。覚悟してついてきてほしい。

—

1. コアアーキテクチャ:なぜSpannerのバックアップは「一瞬」で終わるのか?

従来のRDBで数TBのバックアップを取ろうとすれば、ディスクの読み込み(I/O)が発生し、バッファプールが汚れ、クエリのレイテンシが跳ね上がる。最悪の場合、ロック競合で本番サービスが停止する。

しかし、Cloud Spannerはその常識を完全に破壊している。

Colossusスナップショットとの密結合

Cloud Spannerのストレージ層は、Googleの分散ファイルシステムであるColossus(Google File Systemの次世代版)の上に構築されている。Spannerのデータ実体(SSTable形式のファイル群)は、Colossus上に分散配置されている。

バックアップ作成APIを叩いた瞬間、Spannerは何をしているのか?
データを物理的にコピーしているわけではない。 行われているのは、Colossusレベルでの「メタデータのポインタ操作(Copy-on-Writeスナップショットの作成)」だけだ。

[Spanner Node] —> [Colossus (分散ファイルシステム)]
│
├─ 📂 Base Data Files (SSTable)
│ ▲
│ │ (メタデータ参照)
└─ 📸 Backup Snapshot (ポインタのみ作成・物理コピーなし)

これが、データサイズが10GBであろうが、10TBであろうが、バックアップ生成が数秒から数十秒で完了する理由である。ストレージへの追加負荷もほぼゼロ。本番トラフィックを阻害することは理論上あり得ない。

—

2. リストア(復元)のメカニズムと注意すべき「罠」

バックアップが速いのはわかった。では、リストアはどうなるか?
バックアップからのリストアもまた、Colossus上のスナップショットからのポインタ再構築として行われるため、非常に高速だ。数分〜数十分で新しいSpannerインスタンスとしてデータが蘇る。

しかし、ここで実務上の重大な設計判断が迫られる。

新規インスタンスへのリストア vs インプレースリストア

Cloud Spannerでは、バックアップからデータを復元する際、「既存のデータベースを上書きする(インプレース)」ことはできない。必ず「新しいデータベース名」を指定してリストアを行う必要がある。

gcloudコマンドによるリストアの例
gcloud spanner databases restore projects/my-project/instances/my-instance/backups/my-backup \
–destination-instance=my-instance \
–destination-database=restored-db

【テクニカルリードからの教訓:接続先切り替えの設計】
リストア品が別データベースとして生成されるため、アプリケーション側の接続文字列(Connection String)の切り替え、あるいはDNS/ルーティングの切り替えが必須となる。
「リストア完了=即座にサービス復旧」ではない。アプリケーション層のコンフィグ更新や、コネクションプールの再初期化タイムラグを考慮したDR(災害復旧)ランブックを事前に用意しておくこと。

—

3. ポイントインタイムリカバリ(PITR)の真髄

バックアップのスケジュール運用も重要だが、実務で真価を発揮するのは PITR(Point-In-Time Recovery / タイムトラベル機能) だ。

Cloud Spannerは、過去最大7日間(最長720時間以内)の任意のミリ秒単位のタイムスタンプを指定して、その時点の状態をクエリしたり、別データベースとして復元したりできる。

PITRの内部動作原理

Spannerは、すべての書き込みに対して内部的なタイムスタンプ(MVCC: 多版同時実行制御)を付与している。古いバージョンのデータはガベージコレクション(GC)によって削除されるが、PITRの保持期間内(デフォルトでは最大7日)は、古いSSTableブロックがColossus上で保持され続ける。

したがって、「昨日誤って `DROP TABLE` を実行してしまった」という人為的ミス(ヒューマンエラー)が発生した場合でも、事故の1秒前のタイムスタンプを指定してリストアすれば、データロスを最小限に抑えて完全復活させることが可能だ。

タイムトラベルクエリの例(SQL)

バックアップテーブルからデータを直接復元せずとも、過去の特定時点のデータを参照したい場合は、SQLの `FOR SYSTEM_TIME` 句が使える。

— 202X年10月1日 12:00:00 UTC 時点のデータを安全に参照する
SELECT
FROM Users
FOR SYSTEM_TIME AS OF TIMESTAMP ‘202X-10-01T12:00:00Z’
WHERE user_id = ‘target_user_001’;

このクエリを使えば、バッチ処理で誤更新されたデータをリアルタイムに検出し、本番テーブルへ安全にロールバックするスクリプトを書くことができる。

—

4. 堅牢な設計パターン:プロダクション環境でのバックアップ戦略

では、実際のシステム設計において、どのようにバックアップを組み込むべきか。私のチームで標準採用している設計パターンを共有しよう。

パターンA:自動スケジュールバックアップ(Cloud Scheduler + Cloud Functions / Workflows)

Spanner自体にはネイティブな「cron形式のバックアップスケジュール機能」がない(※GCPの機能拡張を注視せよ)。そのため、実務では Cloud Scheduler と Cloud Functions (または Cloud Workflows) を組み合わせて、定期的なバックアップ生成を自動化する。

【設計のポイント:世代管理(Retention)】
無制限にバックアップを作り続けるとストレージコストが爆発する。スクリプト側で「過去7日分のデイリーバックアップ」「過去4週分のウィークリーバックアップ」を維持し、古いバックアップを自動削除するライフサイクル管理を必ず実装すること。

バックアップ作成のPythonサンプル (google-cloud-spanner)
from google.cloud import spanner_admin_database_v1
import time

def create_spanner_backup(request):
client = spanner_admin_database_v1.DatabaseAdminClient()

project_id = “your-gcp-project”
instance_id = “production-instance”
database_id = “core-db”

timestamp = time.strftime(“%Y%m%d-%H%M%S”)
backup_id = f”auto-backup-{database_id}-{timestamp}”

parent = client.instance_path(project_id, instance_id)
database = client.database_path(project_id, instance_id, database_id)

# バックアップ保持期限(例: 14日後)
expire_time = time.time() + (14 24 60 60)

backup = {
“database”: database,
“expire_time”: {“seconds”: int(expire_time)}
}

operation = client.create_backup(
parent=parent,
backup_id=backup_id,
backup=backup
)

# 非同期オペレーションの完了を待つ
result = operation.result(timeout=600)
print(f”Backup created: {result.name}”)
return “OK”, 200

パターンB:マルチリージョン構成におけるバックアップの考え方

マルチリージョン(例: `nam-eur-asia1` や `us-central1` など)で構成されたSpannerインスタンスは、すでに複数のデータセンター間で同期レプリケーションされており、ハードウェア障害に対して極めて堅牢だ。

「じゃあバックアップは不要か?」
――答えはNOだ。 ハードウェア障害には耐えても、「アプリケーションバグによる大規模なデータ破損(全件UPDATE漏れなど)」や「悪意ある管理者によるデータ削除」からはマルチリージョンレプリケーションも守ってくれない。
マルチリージョン環境であっても、論理的・人為的災害に備えた独立したバックアップ(およびPITRの有効化)は必須要件である。

—

5. パフォーマンスとコスト上の注意点(アーキテクトの視点)

最後に、コストとパフォーマンスの最適化について釘を刺しておこう。

1. ストレージコストの二重計上リスク
Cloud Spannerのバックアップデータは、Colossus上で圧縮され保持されるが、当然ながらバックアップサイズに応じたストレージ料金が発生する。不必要に長い保持期間を設定すると、ストレージ費用が本番DBの容量を超えていく。RTO/RPO要件とコストのトレードオフをビジネス側と合意しておけ。

2. リストア先のノード数(Compute Capacity)に注意
バックアップからのリストア時、リストア先の新規インスタンスのノード数(またはCU: Processing Units)を小さく設定しすぎると、リストア後のデータロードや初期クエリ処理でCPUスパイクを引き起こす。大規模データベースをリストアする際は、本番同等か、あるいは一時的に十分なキャパシティを割り当ててからリストアを実行するべきだ。

—

結びにかえて

Cloud Spannerのバックアップとリストアは、単なる「データの保存機能」ではない。Colossusの分散アーキテクチャと密接に連携した、「攻めと守りのための高可用性インフラストラクチャ」である。

仕組みの本質を理解していれば、障害時にも焦らず、正確なRTOを叩き出し、ビジネスを守り抜くことができる。
設計レビューで「なぜこのバックアップ戦略なのか?」と問われたとき、今日の私の話を自信を持ってロジカルに語ってほしい。

健闘を祈る。

コメント

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