
2025/12/23 16:58
PostgreSQL 18で実現する即時データベースクローン
RSS: https://news.ycombinator.com/rss
要約▶
Japanese Translation:
PostgreSQLは、ファイルを直接コピーすることでほぼ即座にデータベースをクローンできるようになりました。この方法は、基盤となるファイルシステムがreflinks(新しいデータを書き込まずにファイルを複製する手法)をサポートしている場合、さらに高速になります。PostgreSQL 15では、データベースの作成には従来、大量のチェックポイント操作が必要で、システムを停止させることもありました。新しい
FILE_COPY戦略は既存のデータベースファイルを単にコピーするだけでこの処理をスキップします。デフォルトではPostgreSQLはtemplate1データベースからクローンし、物理ブロックをソースと共有するゼロコピークローンを実行します。クローン後、両方のデータベースは同じ論理サイズ(pg_database_size('source_db') ≈ pg_database_size('fast_clone'))を報告します。いずれかのデータベースが更新されると、コピー・オン・ライトで全8 KBページが複製され、結果として二つのデータベースは物理的に分離していきます。
ベンチマークでは、
FILE_COPYを使用した6 GBのデータベースクローンに約212 msかかるのに対し、従来方法では67 000 msかかります。ストレージがreflinks(例:ZFS、reflink対応XFS、APFS)をサポートしている場合、file_copy_method = clone を設定すると追加ディスク領域なしで即時クローンが可能です。この技術はソースデータベースがアイドル状態であり、単一ファイルシステム内にある場合のみ有効です。別マウント上のテーブルスペースは物理コピーへフォールバックします。PostgreSQLが進化するにつれて、reflink対応システムへの広範なサポートがこの恩恵を拡大する可能性がありますが、ファイルシステムを抽象化したクラウドプロバイダーはまだ即時クローンを提供できません。本文
長時間走るマイグレーションスクリプトを見たことがありますか?
- データベースのコピーが「今すぐ壊れそう」なほど大きくなると、
とリストアはもう限界です。pg_dump - テストごとに「新しいデータベースを作るだけ」で済ませたい。
- 再現性のあるスナップショットでテスト実行毎に環境をリセットしたい(そう、SQL が面倒な時こそ学習環境をリセットする必要がありますよ)。
小さめのデータベースなら
pg_dump と restore で十分ですが、数百 MB・GB に達すると「コピーするだけ」では負荷が大きくなります。
テンプレートシステム
PostgreSQL はデフォルトで
template1 に接続します。見落としやすい点は、実際には完全に隠れたテンプレートシステムが存在することです。
CREATE DATABASE dbname;
を実行すると、PostgreSQL は静かに標準のシステムデータベース
template1 をクローンし、まるで
CREATE DATABASE dbname TEMPLATE template1;
と書いたように振舞います。
本当の強力な点は
の代わりに任意のデータベースを指定できることです。template1
詳細は Template Database のドキュメントをご覧ください。
この記事では、テンプレートシステムを「即時・ゼロコピー」データベースクローンマシンに変える数点のチューニング方法を紹介します。
CREATE DATABASE … STRATEGY
PostgreSQL 15以前は、テンプレートから新規 DB を作成する際、ファイルレベルで操作していました。
安定性を確保するためには、
CHECKPOINT で全ての未フラッシュ操作をディスクに書き込み、一貫したスナップショットを取る必要がありました。その結果、大量の I/O スパイク(「Checkpoint Storm」)が発生し、本番トラフィックが停止することも。
バージョン 15 で新しいパラメータ
CREATE DATABASE … STRATEGY = [strategy]
を導入し、テンプレートからの DB 作成時のデフォルト挙動を変更しました。
現在のデフォルトは
WAL_LOG です。これはブロック単位で WAL(Write‑Ahead Log)経由でコピーする方式で、I/O がシーケンシャルになり(スムーズ)、レイテンシが急上昇せずに同時実行をサポートします。ただし、大きなテンプレートでは遅くなることがあります。
STRATEGY を使うと元の FILE_COPY 方法に戻すことも可能です。高速ですが、同時実行性は低下します。PostgreSQL 18 からはさらに多様なオプションが追加されました。
FILE_COPY
FILE_COPY は OS のファイル操作をプロキシするだけなので、OS がそれらのファイルをどのように扱うかを変更できます。
従来のファイルシステム(ext4 など)では PostgreSQL はソースファイルの全バイトを読み取り、新しい場所へ書き込む「物理コピー」を行います。
PostgreSQL 18 からは
file_copy_method を設定することでそのロジックを切り替えられます。デフォルトは copy です。
ZFS、XFS(reflink 対応)、APFS などのモダンファイルシステムでは、
clone に設定すると CLONE(Linux なら FICLONE)操作を利用し、ほぼ瞬時にコピーでき、追加ストレージも必要ありません。
手順は以下です:
- サポートされる OS を使用する(Linux の XFS/ZFS、macOS の APFS、FreeBSD の ZFS)。
- PostgreSQL をそのファイルシステム上で稼働させる。
でpostgresql.conf
と設定。file_copy_method = clone- 設定をリロードする。
ベンチマーク
コピー対象のダミーデータが必要です(ここだけは待ち時間があります)。
約6 GB のデータベースを作成します:
CREATE DATABASE source_db; \c source_db CREATE TABLE boring_data ( id serial PRIMARY KEY, payload text ); -- 5,000 万行生成 INSERT INTO boring_data (payload) SELECT md5(random()::text) || md5(random()::text) FROM generate_series(1, 50000000); CHECKPOINT;
データベースサイズは次のように確認できます:
Name | source_db Owner | postgres Size | 6289 MB
デフォルト(WAL_LOG)
\timing を有効にして実行すると:
CREATE DATABASE slow_copy TEMPLATE source_db;
比較的遅いストレージで約 67 秒 かかります。
Clone 方法
設定がスピード向きになっていることを確認します:
SHOW file_copy_method; -- clone
そしてクローン作成:
CREATE DATABASE fast_clone TEMPLATE source_db STRATEGY = FILE_COPY;
時間は 約212 ms。大幅に高速化です!
クローンデータの取り扱い
シンプルな部分でしたが、背後で何が起きているかを見てみましょう。
file_copy_method = clone で DB をクローンすると、PostgreSQL は実際にデータを複製しません。ファイルシステムは新しいメタデータエントリを作成して同じ物理ブロックを指すだけです。そのため、両方のデータベースは「ストレージを共有」しています。
混乱するかもしれません。PostgreSQL にサイズを尋ねると:
SELECT pg_database_size('source_db') AS source, pg_database_size('fast_clone') AS clone;
どちらも約6 GB と報告されます。これは 論理的なサイズ(各 DB が「持っている」データ量)です。
実際に書き込みを開始すると、PostgreSQL はタプルを直接更新せず、新しいバージョンを書き込み、古いものは死んだ状態にします。
共有ページへの書き込みは 8 KB ページ単位でのコピー・オン・ライト(COW)を引き起こします。
- 古いタプルがあるページ
- 新しいタプルを書き込むページ
- インデックスページ(変更した列がインデックス化されている場合)
- FSM と可視性マップのページ
さらに
VACUUM で死んだタプルを掃除すると、より多くのページが触れられます。結果として、クローンと元はストレージ上で急速に分離します。
XFS の証拠
データベース OID と relfilenode を使って、両 DB が同じ物理ブロックを共有しているか確認できます:
sudo filefrag -v /var/lib/postgresql/18/main/base/16402/16404
(…共有領域が表示される…)
その後いくつか行を書き換えると:
UPDATE boring_data SET payload = 'new value' || id WHERE id IN (SELECT id FROM boring_data LIMIT 20);
filefrag を再実行すると、最初の領域が shared フラグを失い、2つの DB がその部分で分離したことが確認できます。
注意すべき点
-
アクティブ接続
クローンは魅力的ですが、ソースデータベースにはクローン中に接続が存在してはいけません。これは PostgreSQL の制限であり、ファイルシステムの問題ではありません。
本番環境では通常、ライブ DB を直接クローンする代わりに専用のテンプレート DB を作成します。 -
単一ファイルシステム
クローンは同じファイルシステム内でのみ機能します。テーブルスペースが複数のマウントポイントにまたがる場合、クローンは通常の物理コピーにフォールバックします。 -
マネージドクラウド環境
AWS RDS や Google Cloud SQL など、多くのマネージドサービスでは底層ファイルシステムへのアクセスが制限されているため、この設定は利用できません。
自前の VM やベアメタルサーバーなら試してみる価値があります。