インフラ・DevOps
大規模並列Postgresバックアップ
Massively Parallel Postgres Backups (planetscale.com)
要約
PlanetScaleは、シャーディングされたPostgresデータベースのバックアップを効率的に行うための大規模並列化技術について解説しています。この手法では、バックアップ専用のEC2インスタンスを各シャードごとに動的に起動し、既存のバックアップからのリストアとWAL(Write-Ahead Log)の再生を組み合わせて、ペタバイト級のデータベースでも数時間でバックアップを完了させます。これにより、本番環境への影響を最小限に抑えつつ、高速かつ信頼性の高いバックアップを実現しています。
全文翻訳
大規模並列Postgresバックアップ
Ben Dicken [@BenjDicken] | 2026年7月31日
12時間ごとに、バックアップシステムは、本番クエリに影響を与えることなく、ビジーなデータベース全体の状態を一貫性のある暗号化されたスナップショットに変換する必要があります。
このようなバックアップは極めて重要であり、同時にほとんどのエンジニアが考えたくないことでもあります。
バックアップを機能させるだけ。
PlanetScaleでの私たちの目標は、PostgresとMySQLのバックアップの取得、スケジュール設定、管理、復元を楽にすることです。
これは外部からお客様が体験することですが、これを内部で実現するには、クラウドインフラストラクチャとDBMSツールの慎重なオーケストレーションが必要です。
特にシャーディングされたデータベースのバックアップは興味深く、バックアップ専用ノードの起動、オブジェクトストレージからのデータ取得、WALの再生などをすべて大規模な並列処理で行う必要があります。
これらの技術により、ペタバイト級のデータベースを数時間で、毎秒50GBを超える速度でバックアップできます。
ここでは、シャーディングされたデータベースを大規模な並列処理で効果的にバックアップする方法を舞台裏から見ていきます。
バックアップのライフサイクル
ここでは、毎秒数十万件のクエリを処理するNeki(シャーディングされたPostgres)データベース(8シャード)の例を示します。
シャーディングが初めての方は、最近の投稿「Making 768 servers look like 1」でその仕組みをご覧ください。
バックアップを取得する最初のステップは、これが最初のバックアップか、以前にバックアップを取得したことがあるかによって異なります。
ここでは、以前に正常なバックアップが取得され、Amazon S3(または他のクラウドの類似オブジェクトストレージ)に保存されていると仮定した、定常状態のケースから始めます。
シャーディングされたPostgresデータベースは、連携して動作する多数の個別のPostgresサーバーであるため、Nekiでの大規模バックアップのビルディングブロックとして通常のPostgresバックアップを使用します。
Postgresでバックアップを取得するには3つの方法があり、これについては以前に詳しく説明しました。
その中でも最良の方法であり、Nekiが使用する方法は、ファイルシステムバックアップとアーカイブされたWrite-Ahead Log(WAL)の再生を組み合わせることです。
手順の概要は以下の通りです。
時刻T1でディスク上のPostgresデータベースファイルのフルバックアップを開始する
バックアップは時刻T2で完了します。T1とT2の間、ディスク上の行は変更されている可能性があります。
T1とT2の間のWAL変更を再生して、変更されたデータを修正する
最終結果をAmazon S3のような別のストレージ場所に保存する
これらの手順はプライマリで直接実行することも、トラフィックを処理するレプリカのいずれかで実行することもできます。
問題は、特に大規模なデータベースの場合、このタスクがかなりの量のIOPSとコンピューティングリソースを消費することです。
私たちの目標は、バックアップが本番クエリの提供に与える影響を最小限に抑えることです。
そのため、私たちは異なるアプローチを取ります。
バックアップを管理するために、シャードごとに新しいEC2インスタンスのセットを起動します。
これらの新しいインスタンスが、バックアップ作業の大部分を担います。
AWSやGCPのようなクラウドでシャーディングされたデータベースを運用しているため、バックアップを完了するために短時間だけ数十または数百のインスタンスを動的に起動することは可能です。
これによりコストはわずかに増加しますが、本番環境への悪影響を最小限に抑える価値があります。
古いバックアップの再利用
次のステップは、各シャードに最新のバックアップを復元することです。
以前のバックアップはオブジェクトストレージに保存されています。
ここではAmazon S3を例として使用しますが、他のクラウド(Google CloudのGCSなど)でも同様です。
これらはここから直接ストリーミングされます。
このアプローチでは、一時的なコンピューティングリソースが必要となり、各シャードのデータがオブジェクトストレージから転送され、再びオブジェクトストレージに戻されます。
このコストを受け入れるのには2つの重要な理由があります。
最新のWALのみがプライマリから取得されるため、本番環境への影響が最小限に抑えられます。
各サイクルで、前のバックアップが復元および再生可能であることが証明されます。
すべてのコピーが完了すると、これらの8台のサーバーは、12時間前の前のバックアップの各シャードの正確な状態を持つことになります。
WALの再生
次に、各シャードの古いバックアップを現在のデータベースの状態に一致するようにキャッチアップする必要があります。
これには、12時間前と現在の間のPostgres Write-Ahead Logからのすべての変更を再生する必要があります。
単純なアプローチは、WALを直接プライマリから取得することです。
これはいくつかの理由で問題があります。
これは本番環境にかなりの影響を与えるでしょう。
高負荷のデータベースで12時間のWALを再生するには、数十分、あるいは1時間以上かかる可能性があります。
WALによってサーバーストレージが過剰に消費されるのを避けるため、WALは継続的にS3にアーカイブされます。
そのため、プライマリノード上には過去12時間分のWAL全体が存在しない可能性があります。
PlanetScaleでは、すべてのPostgresデータベースがwal-gを使用してWrite-Ahead Logを継続的にアーカイブしています。
シャーディングされたNekiデータベースも同様ですが、各シャードには個別のWALアーカイブストリームがあります。
代わりにそこからストリーミングするのはどうでしょうか?
これで問題はほぼ解決します。
残りの問題は、Postgresがセグメントが完了した後にのみWALをアーカイブすることです。
書き込みトラフィックがセグメントをすぐに満たさない場合、5分間のarchive_timeout設定によりセグメントの切り替えが強制され、アーカイブできるようになります。
これにより、最新の変更がまだS3に到達していない可能性があります。
そのため、ハイブリッドアプローチを取ります。
システムは再生の大部分にS3を使用し、その後最後の数分間の変更をプライマリから直接ストリーミングします。
理想的には、この最後のステップは数分や数時間ではなく、数秒で完了します。
このステップをまとめると、シャーディングされたデータベースに戻って次のようになります。
サイクルを完了する
すべてのノードが時刻Tまでレプリケーションをキャッチアップしたら(Tはバックアップ時刻として記録するタイムスタンプ)、WALレプリケーションを停止し、バックアップの時点をフリーズします。
時刻Tは、このバックアップに含まれる正確な時刻(秒単位まで)を知るために保存されます。
フルバックアップは一貫性があり、データが破損していません。
最後のステップは、これらのバックアップを暗号化し、安全に保管するために新しいS3バケットに送信することです。
完了すると、バックアップノードはその目的を果たし、廃止されます。
最初のバックアップ
上記で説明したサイクルは、開始するために12時間前の良好なバックアップがあると仮定していました。
定常状態ではこれは真実ですが、新しいデータベースの場合はそうではありません。
新しいデータベース作成から12時間以内に、pg_basebackupを使用して各シャードのバックアップノードをシードします。
pg_basebackupは、データベースクラスタ全体(データディレクトリ、テーブルスペース、リカバリに必要な設定)の物理バックアップを取得する組み込みのPostgreSQLクライアントユーティリティです。
pg_basebackupの出力を直接アップロードするのはなぜですか?
一時的なバックアップノードをシードするためにのみ使用します。
永続的なバックアップはwal-gで作成され、初期バックアップと定常状態バックアップでフォーマットと復元プロセスを同一に保ちます。
この最初のバックアップの手順は、定常状態のものと似ていますが、わずかに異なります。
シャードごとに1つの新しいEC2インスタンスを起動します。
各インスタンスでpg_basebackupを実行し、プライマリからデータをコピーします。
各インスタンスがプライマリからレプリケートするように設定します。
各インスタンスがキャッチアップするまでWALを再生します。
時刻Tでレプリケーションを停止します。
wal-gを使用して各バックアップをフォーマット、暗号化し、S3にアップロードします。
これが存在すると、将来のすべてのバックアップは、復元 -> キャッチアップ -> 保存のフローで行われます。
速度の必要性
並列処理を多く行う理由の一部は、シャーディングの性質によるものです。
4シャードであれ400シャードであれ、各シャードには独自のPostgresプライマリが含まれているため、堅牢なシステム(通常のPostgresバックアップ)を使用して各シャードで何度も実行する価値があります。
これにより、大規模なデータベースであってもバックアップが非常に高速になります。
バックアップごとに、データ転送の手順は次のようになります。
S3から古いバックアップを復元する
S3とプライマリのハイブリッドを使用してバックアップをキャッチアップする
新しいファイルをS3に戻す
シャーディングされていない32テラバイトのデータベースでこのバックアップサイクルを完了するために必要なことを考えてみてください。
PlanetScaleでのバックアップは圧縮され、S3で保存時に暗号化されて保存されます。
この32テラバイトのデータベースのバックアップは20テラバイトに圧縮されると仮定します。
したがって、次のようになります。
S3から20,000 GBの転送
キャッチアップ(例: 20 GB、10 GBに圧縮)
S3に20,010 GBを戻す
これは、合計転送量約40,030 GBに相当します。
私たちのさまざまなノードとノード間の通信が...