【ITニュース解説】PostgreSQL 19's REPACK CONCURRENTLY swaps a five-second table lock for one under a second
2026年10月14日に「Dev.to」が公開したITニュース「PostgreSQL 19's REPACK CONCURRENTLY swaps a five-second table lock for one under a second」について初心者にもわかりやすく解説しています。
ITニュース概要
PostgreSQL 19の新機能「REPACK CONCURRENTLY」は、テーブルの肥大化解消時の排他ロック時間を数秒から1秒未満へ大幅に短縮した。これにより、処理中もテーブルへの読み書きがほぼ中断されず行える。ただしWAL書き込み量は増え、並列実行数には制限がある点に注意が必要だ。
ITニュース解説
PostgreSQLデータベースを運用する上で、テーブルのサイズが肥大化するという問題は古くから知られていた。例えば、あるテーブルから大量のデータを削除しても、すぐにデータベースのファイルサイズが小さくなるわけではない。これは、PostgreSQLが削除されたデータをすぐにディスクから消去せず、「再利用可能な領域」としてマークする仕組み「VACUUM」を採用しているためだ。この方式は、頻繁なデータ削除と挿入がある環境でパフォーマンスを維持するのに役立つ一方で、長期的には未使用の領域が残り、テーブルの物理的なサイズが大きくなってしまう「テーブルの肥大化(Bloat)」を引き起こすことがあった。
この肥大化したテーブルを物理的に縮小し、未使用領域を解放するためには、「VACUUM FULL」コマンドや、サードパーティ製の拡張機能「pg_repack」を利用する必要があった。しかし、これらのコマンドには大きな課題があった。特にVACUUM FULLは、処理中にテーブル全体に「排他ロック」をかけてしまう。これは、テーブルが完全に書き換えられている間、他のすべてのデータベース操作(データの読み書き)が停止してしまうことを意味する。もし、本番環境でユーザーが頻繁にアクセスするテーブルに対してVACUUM FULLを実行すれば、数秒から数十秒、あるいはそれ以上にわたってサービスが利用できなくなり、ビジネスに大きな影響を与えてしまう可能性があった。pg_repackはこのロック時間を短縮するが、外部ツールであるため導入や管理に手間がかかる。
このような背景から、PostgreSQL 19では、この課題を解決するための新しいコマンド「REPACK」が導入され、特に「CONCURRENTLY」オプションが注目されている。REPACK CONCURRENTLYは、テーブルへのアクセスをほとんど中断することなく、肥大化したテーブルを物理的に再構築し、ディスク領域を解放できる画期的な機能である。
具体的な仕組みを説明しよう。REPACK CONCURRENTLYが実行されると、まずバックグラウンドで元のテーブルの新しいコピーを作成し始める。この間も、元のテーブルへのデータの読み書きは通常通り続けることができる。新しいコピーの作成中に元のテーブルに発生したあらゆる変更(データの追加、更新、削除など)は、「ロジカルデコーディング」という特別な仕組みを使ってリアルタイムで追跡される。そして、これらの変更は新しいコピーにも継続的に適用されていく。新しいコピーが完全に作成され、元のテーブルとの内容が同期された最終段階で、ごく短時間だけ排他ロックがかけられ、古いテーブルと新しいテーブルが入れ替わる。これにより、テーブル全体を再構築する作業の大部分はテーブルロックなしで行われ、ユーザーが体感するサービス停止時間は大幅に短縮される。
実際の実験では、約200万行のデータを持つ肥大化したテーブルを対象に、従来のVACUUM FULLと新しいREPACK CONCURRENTLYのパフォーマンスが比較された。結果は明らかだった。VACUUM FULLが約5秒間、テーブル全体に排他ロックをかけ、その間クライアントからのアクセスは完全にブロックされたのに対し、REPACK CONCURRENTLYではクライアントがブロックされた時間はわずか0.3秒から1秒未満であった。これは、サービス停止時間が約5分の1から10分の1以下に短縮されたことを意味する。複数のクライアントが同時にテーブルにアクセスしている状況でも、REPACK CONCURRENTLYによる個々のクライアントのロック時間はほとんど変わらなかった。これは、ロックがテーブルの入れ替えというごく短い瞬間だけ発生するため、その時間に待機するクライアントが増えても、その瞬間が極端に長くなるわけではないからだ。
もちろん、このロック時間の短縮は「無料」ではない。REPACK CONCURRENTLYは、ロジカルデコーディングという複雑な仕組みを利用するため、VACUUM FULLと比較してより多くの「WAL(Write-Ahead Log)」を生成する。WALとは、データベースへの変更を永続化するために記録される重要なログデータのことだ。WALデータ量が増えるということは、特にデータベースのレプリケーション(複製)環境において、複製先へのデータ転送にかかるネットワーク帯域幅が増加したり、WALの保持期間に影響が出たりする可能性がある。そのため、WALの生成量とレプリケーション環境への影響は、この機能を導入する際に考慮すべき重要な点となる。
また、REPACK CONCURRENTLYには実行上の制約も存在する。この機能は内部的にロジカルデコーディングセッションを使用するため、データベースの「max_repack_replication_slots」という設定で定義された「空きスロット」が必要となる。デフォルトではこのスロットは5つ用意されているが、同時に多くのREPACK CONCURRENTLYを異なるテーブルに対して実行しようとすると、このスロットを使い果たしてしまい、それ以上は実行できなくなる。この場合、データベースは明確なエラーメッセージを返し、処理がハングアップすることはないため、運用上は管理しやすいと言えるだろう。しかし、複数のテーブルに対して一括でメンテナンスを実行するような場合には、この設定値に注意が必要だ。
さらに、PostgreSQL 19では、REPACK CONCURRENTLYの実行状況をリアルタイムで確認できる「pg_stat_progress_repack」という新しいビューも提供される。これにより、現在の処理フェーズや、どれくらいのデータがスキャンされ、どれくらい進捗しているかを具体的な数値で把握できるようになり、「あとどれくらいで完了するか」といった見込みを立てるのに役立つ。これは、VACUUM FULLでは不可能だった、運用上の大きな改善点と言える。
このREPACK CONCURRENTLY機能はまだベータ版であり、正式リリースまでにコマンド名や動作、デフォルト設定などが変更される可能性もある。しかし、そのロック時間短縮のメリットは非常に大きく、現在pg_repackやVACUUM FULLを使ってテーブルの肥大化に対処しているシステムエンジニアにとっては、導入を検討する価値のある新機能だ。本番環境での利用を検討する際には、実際のデータ量や同時書き込み負荷のもとで、その効果やWAL生成量、レプリケーションスロットの消費状況などを事前に十分にテストしておくことが推奨される。これにより、安定したデータベース運用を維持しながら、メンテナンスによるサービス停止時間を最小限に抑えることが可能になるだろう。