PostgreSQL 19 のグラフクエリと無停止REPACKを受託システム開発でどう使うか | GH Media
URLがコピーされました

PostgreSQL 19 のグラフクエリと無停止REPACKを受託システム開発でどう使うか

URLがコピーされました
PostgreSQL 19 のグラフクエリと無停止REPACKを受託システム開発でどう使うか

「特定の顧客に紐づく取引先、その取引先がぶら下がる親会社、さらに同じ親会社を持つ別の顧客——この関連を3階層たどって一覧したい」。中小企業の業務システムでこの種の要望が出るたび、アプリ側で SELECT を何度も投げ、PHP や TypeScript のループでつなぎ合わせて返す実装を見かけます。データが増えるほど画面が重くなり、N+1 を疑って調べると、そもそも関連をたどる処理を全部アプリに背負わせていた、という構図です。一方インフラ側では、論理削除フラグや履歴で膨らんだテーブルの肥大化を VACUUM FULL で解消するために、毎月どこかの深夜にサービスを止めてメンテナンス枠を取っている現場も珍しくありません。

2026年6月4日にリリースされた PostgreSQL 19 Beta 1 は、この二つの痛みにそれぞれ正面から効く機能を載せてきました。SQL標準(ISO SQL:2023 Part 16)のグラフクエリ SQL/PGQ と、テーブルの再編成をオンラインで実行する REPACK … CONCURRENTLY です。InfoQ も PostgreSQL 19 Beta Introduces SQL Graph Queries and Concurrent Table Repacking で、「派手なのはグラフクエリだが、運用担当が本当に喜んでいるのは無停止の再編成のほうだ」と現場の温度感を伝えています。受託で「DBごと設計して引き渡す」立場からは、どちらも見積りと提案の材料になります。本記事では、この二つを発注者の痛みに引き付けて読み解きます。なお正式版(GA)は例年どおり秋ごろの見込みで、本記事の構文はあくまで Beta 時点の方向性として扱ってください。

関連データの分析を「アプリのループ」からDBへ戻す

業務システムで重くなりがちな処理の多くは、「関係をたどる」クエリです。顧客→取引→請求、社員→所属部署→上長、商品→カテゴリ→関連商品。こうした多段の関連を、これまでの PostgreSQL では JOIN を積み重ねるか、再帰CTE(WITH RECURSIVE)で書いてきました。再帰CTEは強力ですが、「2ホップ以上の可変長の経路」「循環の検出」「経路そのものの取得」あたりになると、書き味が一気に難しくなります。結局アプリ側でループして関連をたどる実装に逃げ、それが N+1 と画面の重さを生む——という流れは、受託案件の引き継ぎでよく踏む地雷です。

PostgreSQL 19 の SQL/PGQ は、既存のリレーショナルテーブルの上に「プロパティグラフ」を定義し、パターンマッチング構文で関連をたどるという発想です。重要なのは、グラフ専用のストレージや別DBへの移行が不要で、定義したグラフへのクエリは内部で通常のリレーショナル演算に書き換えられ、既存のインデックスをそのまま使う、という点です。データを二重に持つ必要がない。これは「分析のためだけに Neo4j を別途立てて同期する」案を一つ消せる、という意味で、運用コストと障害点を一つ減らせます。

書き味のイメージを、Beta 時点の方向性として示します。まず、顧客テーブルと取引テーブルから、頂点(VERTEX)と辺(EDGE)を持つプロパティグラフを定義します。

-- 既存テーブルの上にグラフを「定義」するだけ(データの複製は発生しない)
CREATE PROPERTY GRAPH biz_graph
  VERTEX TABLES (
    customers   LABEL customer,
    companies   LABEL company
  )
  EDGE TABLES (
    -- deals が customers と companies を結ぶ辺になる
    deals
      SOURCE KEY (customer_id) REFERENCES customers (id)
      DESTINATION KEY (company_id) REFERENCES companies (id)
      LABEL trades_with
  );

その上で、「ある顧客から取引でつながる会社を、同じ親を持つ別顧客まで含めてたどる」ようなクエリを、パターンで書きます。頂点は ( )、辺は -[ ]-> で表現し、矢印の向きが関連の向きを表します。

-- 顧客 c から取引でつながる会社 co を取得(1ホップ)
SELECT customer_name, company_name
FROM GRAPH_TABLE (biz_graph
  MATCH (c IS customer)-[IS trades_with]->(co IS company)
  WHERE c.status = 'active'
  COLUMNS (c.name AS customer_name, co.name AS company_name)
);

同じことを再帰CTEで多段にたどろうとすると、結合の中間結果を自分で組み立て、重複や循環を UNION と訪問済み判定で抑え込む必要があります。SQL/PGQ では、可変長の経路や経路情報の取得を、パターン側の語彙で表現していく方向です(Beta 時点では対応範囲が版によって変わり得るため、実装で使う際は対象バージョンのドキュメントで確認してください)。組織図のように「親をたどる深さが行ごとに違う」データを扱うとき、再帰CTEの可読性で苦労した経験がある人ほど、この差は刺さるはずです。

受託の観点で言えば、これは 「分析要件をアプリのコードに溜め込まず、DBの宣言的なクエリに寄せる」 設計判断を後押しします。関連のたどり方が仕様変更で増えても、グラフ定義とクエリの修正で吸収でき、アプリ側のループを書き直す範囲が小さくなる。テーブル設計をどう正規化しておくかという土台の話とも地続きで、論理削除や状態管理をどう持つかは 論理削除をやめて状態テーブルへ — DB再設計の受託(GH Media) で扱った観点が、そのままグラフ定義のしやすさにつながります。

「深夜の停止メンテ」を無くす REPACK CONCURRENTLY

もう一方の主役が、テーブル再編成のオンライン化です。PostgreSQL は追記型の MVCC を採用しているため、更新・削除を繰り返すテーブルは不要タプルで物理的に膨らみます。通常の VACUUM は領域を「再利用可能」にしますが、OS にディスクを返したり物理的に詰め直したりはしません。これを本格的に解消するのが VACUUM FULLCLUSTER ですが、いずれも処理中ずっとテーブルに排他ロックを取るため、その間そのテーブルへの読み書きが止まります。だから「深夜にサービスを止めてメンテ枠を取る」運用になる。これが発注者にとっては「なぜ定期的にサービスが止まるのか」という、地味だが消えない不満になります。

これまでは拡張機能 pg_repack がこの問題の定番解でした。テーブルを裏でコピーし、その間の変更をトリガーで追いかけ、最後の一瞬だけロックして入れ替える。実運用で重宝してきた一方、拡張のインストールと権限、バージョン追従、マネージドDBでの利用可否といった「拡張ゆえの面倒」がついて回りました。

PostgreSQL 19 はこの仕組みを本体に取り込みます。新コマンド REPACKVACUUM FULLCLUSTER を統合し、CONCURRENTLY を付けるとオンラインで動きます。pg_squeeze 由来のコードをベースに、論理デコーディングで処理中の変更を捕捉し、新しいテーブルへコピーし終えた後にその差分を適用、排他ロックは最後の入れ替え(lock-and-swap)の一瞬だけ、という設計です。

-- 19以前:処理中ずっと排他ロック(=事実上の停止メンテ)
VACUUM FULL orders;

-- 19:オンラインで再編成。排他ロックは最後の一瞬だけ
REPACK orders CONCURRENTLY;

-- 物理順序を特定インデックスに合わせたい場合(旧 CLUSTER 相当)
REPACK orders USING INDEX orders_created_at_idx CONCURRENTLY;

REPACK はインデックスを指定しなければ VACUUM FULL 相当、USING INDEX を付ければ CLUSTER 相当として振る舞う、という整理です。発注者向けの言葉に翻訳すれば、「肥大化したテーブルを掃除するために、もうサービスを止めなくてよくなる」。これは引き渡し後の運用 SLA に直接効く話で、提案書に「定期メンテナンスの無停止化」と一行書けるかどうかは、保守契約の説得力を左右します。

操作ロックディスク返却19での書き方
VACUUM(通常)弱い(並行可)しない(再利用化のみ)従来どおり
VACUUM FULL排他(処理中ずっと)するREPACK ... CONCURRENTLY で無停止化
pg_repack(拡張)ほぼ無停止する本体の REPACK CONCURRENTLY に集約可

可用性を本気で求める案件では、再編成の無停止化だけでなく、待機系やフェイルオーバーまで含めた設計が必要になります。そこは AlloyDB のホットスタンバイで作るDR設計(GH Media) で扱った Postgres 系の冗長構成と合わせて考えると、メンテ起因の停止と障害起因の停止の両方を、運用設計として一枚に描けます。

受託で「採用する/しない」をどう判断するか

新機能が出ると「使えますか」と聞かれますが、受託として本当に答えるべきは「このプロジェクトの引き渡し品質と保守コストに効くか」です。判断の軸を二つに絞ると、次のようになります。

一つ目はグラフクエリ。関連をたどる要件が今後増えそうか、すでにアプリ側のループで重さが出ているか、を見ます。あるBtoB卸売の受発注システム(社名は伏せます)では、得意先と仕入先と商品の三者の関連分析が、もともとアプリ側で多段の SELECT を回す実装になっており、取引先が数千社を超えたあたりから一覧画面が体感で重くなっていました。ここはまさに SQL/PGQ で関連のたどりをDB側へ戻し、アプリのループを薄くできる典型です。ただし PostgreSQL 19 は GA 前の Beta であり、本番投入は GA を待つのが原則。今は「正式版で何が楽になるか」を見越してスキーマと正規化を整えておく準備フェーズと位置づけます。検索や全文の重い処理を別基盤に逃がす判断とも絡むので、その線引きは OpenSearch Serverless で作る次世代の検索基盤(GH Media) の整理も参考になります。

二つ目は REPACK。これは「今すぐ効く改善の予約」として扱えます。すでに pg_repack で無停止再編成を回しているなら、19 移行後は本体機能へ集約でき、拡張の運用負担を一つ減らせる。VACUUM FULL のたびに停止メンテを入れている現場なら、19 移行が「停止メンテそのものを無くす」提案になります。なお REPACK CONCURRENTLY は論理デコーディングを使うため、空き容量・WAL・レプリケーション構成への影響を事前に検証しておくべきで、ここを詰めるのが受託側の腕の見せ所です。MySQL/MariaDB 側で同種の運用近代化を考える案件もあり、そちらは MySQL 9.7 LTS で進める中小企業の受託モダナイズ(GH Media) に切り分けています。

次の一手として、まずは手元の本番に近いデータで、関連分析クエリのうち「アプリのループで重い箇所」を一つ棚卸しすることをおすすめします。そこが SQL/PGQ で置き換わる候補になり、移行の費用対効果を数字で語れるようになります。グリームハブでは、PostgreSQL を使った業務システムの新規開発から、既存DBの再設計・無停止メンテナンス化・性能改善まで、DBごと設計して引き渡す受託を行っています。「うちの関連分析やメンテ運用に19は効くのか」を一緒に見立てるところからで構いません。ご相談はお問い合わせからどうぞ。

Sources

URLがコピーされました

グリームハブ株式会社は、変化の激しい時代において、アイデアを形にし、人がもっと自由に、もっと創造的に生きられる世界を目指しています。

記事を書いた人

鈴木 翔

鈴木 翔

技術の可能性に魅了され、学生時代からプログラミングとデジタルアートの分野に深い関心を持つ

関連記事