---
title: "PostgreSQL 19 におけるモニタリングの新機能"
date: "2026-09-28T08:42:19.392Z"
author: "Gülçin Yıldırım Jelínek"
category: "Engineering"
excerpt: "PostgreSQL 19 におけるモニタリングの新機能をご紹介します。"
---

# PostgreSQL 19 におけるモニタリングの新機能

PostgreSQL 19 のリリースが間近に迫っており、私は間もなく開催される [PostgreSQL Conference Europe](https://2026.pgconf.eu/) でオブザーバビリティの改善について登壇する予定です。また、カンファレンス最終日の10月23日(金)に実施される [Community Events Day](https://2026.pgconf.eu/community-day/) の一環として、PostgreSQL Observability Summit の共同主催者も務めています。そこで、この機会に私の考えをブログ記事にまとめておこうと考えました。

***免責事項:** 本稿執筆時点で PostgreSQL 19 はまだベータ版であるため、以下の詳細の一部は正式リリースまでに変更される可能性があります。19.0 が一般提供(GA)された後は、[リリースノート](https://www.postgresql.org/docs/19/release-19.html)が最終的な情報源となります。*

前置きはこれくらいにして、私が特に有用かつ興味深いと感じている PostgreSQL 19 のモニタリングおよびオブザーバビリティに関する改善点を見ていきましょう。

## ロック競合がデフォルトで可視化されるように {#lock_contention_is_now_visible_by_default}

`log_lock_waits` は、セッションがロックを取得するために `deadlock_timeout`(デフォルトは1秒)を超えて待機した際にメッセージをログ出力するかどうかを制御します。デッドロックのチェックは比較的コストが高いため、そのチェックを実行するまでに待機する時間を設定することが推奨されており、理想的には通常のトランザクション処理時間より長く設定します。これが `deadlock_timeout` と `log_lock_waits` の関係です(Robert Haas 氏は両者を分離することすら[提案](https://postgr.es/m/CA+TgmobPaKeiXQ3q2Qb-ToLGPxy1kkOwi1AS16ZS0ryPKvPHTw@mail.gmail.com)していました)。要するに、`log_lock_waits` を有効にすると Postgres 向けの低コストなロック競合検出器として機能するのですが、これまでデフォルトは無効になっていました。

PostgreSQL 19 ではデフォルトが `on` に変更されます(コミット [`2aac62be8`](https://postgr.es/c/2aac62be8)、Laurenz Albe 氏)。コミットメッセージに書かれた以下の論理には大いに賛同できます。

> 誰かがロック待ちで1秒以上停止しているなら、それはほぼ確実にログに記録する価値のある問題です。

## プロセスタイプごとにログの詳細度を変更可能に {#different_log_verbosity_per_process_type}

これまで `log_min_messages` は単一のグローバル設定項目でした。checkpointer から `DEBUG2` レベルの出力を得ようとすると、あらゆるプロセスから `DEBUG2` が出力されてしまい、必要な行を探し出すのに一苦労していました。

PostgreSQL 19 からは、`log_min_messages` でカンマ区切りの `process type:level` ペアのリストを受け付けるようになり、さらにリストに記載されていないすべてのプロセスタイプに適用する必須のレベルを1つ指定できるようになりました。レベル単体の指定も引き続き有効なため、従来の構文もそのまま動作します。

<pre><code type='click-ui' language='sql'>
-- checkpointer は DEBUG2、autovacuum は DEBUG1、その他はすべて WARNING
ALTER SYSTEM SET log_min_messages = 'warning, checkpointer:debug2, autovacuum:debug1';
</code></pre>

認識されるプロセスタイプは、`archiver`、`autovacuum`、`backend`、`bgworker`、`bgwriter`、`checkpointer`、`checksums`、`ioworker`、`postmaster`、`slotsyncworker`、`startup`、`syslogger`、`walreceiver`、`walsender`、`walsummarizer`、`walwriter` です。なお、`autovacuum` にはランチャーとワーカーの両方が含まれます。

## autoanalyze のログ出力を分離 {#separate_logging_for_autoanalyze}

これまで `log_autovacuum_min_duration` は、autovacuum による VACUUM と ANALYZE の両方の実行に対するログ出力を制御していました。しかし、これらは大きく異なる操作です。通常、autoanalyze の実行時間ははるかに短いため、低速な VACUUM を捕捉するように閾値を調整すると、ほとんどすべての ANALYZE 処理のログが静かに捨てられてしまっていました。

PostgreSQL 19 では `log_autoanalyze_min_duration` が追加され、ANALYZE のログ出力を制御できるようになりました。これに伴い、`log_autovacuum_min_duration` は VACUUM のログ出力のみを制御するようになります。どちらもデフォルト値は `10min` で、`0`(すべてログ出力)および `-1`(無効化)を設定可能です。また、テーブルごとのオーバーライドも設定できます。

<pre><code type='click-ui' language='sql'>
-- グローバル設定に関係なく、このテーブルではすべての autoanalyze をログに記録
ALTER TABLE events SET (log_autoanalyze_min_duration = 0);
</code></pre>

**アップグレード時の注意点:** ツールで autovacuum のログをパースしている場合、アップグレード後は `log_autovacuum_min_duration` だけでは ANALYZE の実行が記録されなくなる点に注意してください。また、UI 上でログパラメータを表示している場合は、`log_autoanalyze_min_duration` も表示できるようにすることを検討してください。

## VACUUM と ANALYZE のログに WAL のフルページ書き込みバイト数を追加 {#wal_full_page_write_bytes_to_vacuum_and_analyze_logging}

フルページイメージは WAL 容量の大半を占めることが多く、VACUUM はこれを大量に生成します。理由を詳しくご存じない方のために簡単に説明します。チェックポイント後に初めてページが変更される際、Postgres はわずかな変更内容だけでなく 8 kB のページ全体を WAL に書き込みます。これにより、クラッシュリカバリ時にディスク上の断片化(torn page)した可能性があるページを信頼せずに済むようにしています。つまり、前回のチェックポイント以降にそのページへ初めてアクセスした場合、わずか 50 バイトの行の更新であっても約 8 kB の WAL が生成される可能性があります。VACUUM は多数のコールドページを巡回して更新するため、こうした書き込みが大量に発生します。これが、アクセス頻度の低い巨大なテーブルをバキュームすると、実際に変更されたデータ量よりもはるかに多くの WAL が生成される理由です。

Postgres はこれまでフルページイメージの件数(`wal_fpi`)をレポートしていましたが、そのサイズは記録していませんでした。そのため、実際のバイト数を知るには `pg_waldump` や `pg_walinspect` を使って WAL を詳しく調べる必要がありました。

PostgreSQL 19 では、Shinya Kato 氏の一連のパッチによって新たなカウンタである `wal_fpi_bytes` が追加されました。この値は、`pg_stat_wal`(メトリクスコレクターがポーリングする対象)を通じてクラスター全体で確認できるほか、`pg_stat_get_backend_wal()`(どの接続がこれほど大量の WAL を生成しているのか調べる際に `pg_stat_activity` と結合するのに便利です)でセッションごとに、`EXPLAIN (ANALYZE, WAL)` でクエリごとに、そして VACUUM と ANALYZE のログ出力で処理ごとに確認できます。ログ出力は以下のようになります。

<pre><code type='click-ui' language='bash'>
WAL usage: 2841 records, 1904 full page images, 15735621 bytes,
           15242880 full page image bytes, 3 buffers full
</code></pre>

`wal_fpi_bytes` を `wal_bytes` と比較することで、WAL のうちどれだけの割合がフルページイメージであるかがわかり、`wal_compression`、チェックポイント間隔、メンテナンススケジュールの見直しのどこに改善の余地があるかを判断できます。

## 新しい待機イベント:WAL LSN と COPY I/O {#new_wait_events_wal_lsns_and_copy_io}

Postgres 19 では、従来は確認が難しかった待機を `pg_stat_activity` で可視化できるようにする新しい待機イベントがいくつか追加されました。

新しい `WAIT FOR` コマンド(これについては別のブログ記事で詳しく解説する予定です)の基盤機能の一部として、セッションが WAL の特定の Log Sequence Number(LSN)への到達を待機しているタイミングを PostgreSQL がレポートできるようになりました。新しい `WaitForWalWrite` 待機イベントは WAL が書き込まれるのを待機している状態を対象とし、既存の `WaitForWalFlush`(従来はプライマリのみ)はスタンバイも対象とするようになりました。これらは `WaitForWalReplay`(名前のとおり、WAL が再生されるのを待機するイベント)とともに、セッションが WAL 処理のどの段階(write → flush → replay)で待機しているかを特定できるようにします。

また、Postgres 19 では `COPY` 周りの同様のオブザーバビリティの不足も解消されました。従来、ファイル、パイプ、またはプログラムを対象とした `COPY FROM/TO` 処理は、待機イベントをまったく伴わずに読み取りや書き込みを行っていたため、バックエンドが停止していても状況を把握できませんでした。現在は、新しい `CopyFromRead` および `CopyToWrite` 待機イベントによってこれらのパスが観測可能になっています。ワイヤプロトコル経由の `COPY` はすでに `ClientRead`/`ClientWrite` でカバーされていたため、これにより一括ロードや ETL パイプラインで一般的に使用されるファイル、パイプ、プログラムのパスが網羅されることになります。

## リモート Postgres サーバーからのメッセージログを改善 {#better_logging_for_messages_from_remote_postgres_servers}

これは細かな変更点ですが、ロジカルレプリケーションや FDW を多用する環境を運用している方には大いに役立つはずです。リモートサーバーから(レプリケーション、`postgres_fdw`、または `dblink` 接続を介して)送られる NOTICE や WARNING などのメッセージは、従来、ローカルサーバーの `stderr` に直接出力されていました。そのため、`log_line_prefix` などの通常のログフォーマットが適用されず、前後のログエントリと関連付けるのが困難でした。

PostgreSQL 19 では、これらのメッセージがローカルサーバーのメッセージと同様に `ereport()` を経由してルーティングされるようになります。通常のログフォーマットが適用され、送信元を示すプレフィックスとして `received message via replication` または `received message via remote connection` が付与されます。これにより、サーバーをまたいだログの関連付けが大幅に容易になります。

## pg_get_multixact_stats() による multixact のアクティビティ把握 {#multixact_activity_with_pg_get_multixact_stats}

複数のトランザクションが同一のタプルをロックした際に Postgres が生成する Multixact(外部キーのチェックや `SELECT ... FOR SHARE` を想定してください)は、長年オブザーバビリティの盲点となっていました。`pg_multixact/members/` がディスクを消費し始めた際、取れる手段といえばファイルシステムの調査と推測くらいのものでした。

PostgreSQL 19 では `pg_get_multixact_stats()` が追加されます。

<pre><code type='click-ui' language='sql'>
SELECT *, pg_size_pretty(members_size) AS members_size_pretty
FROM pg_get_multixact_stats();

 num_mxids | num_members | members_size | oldest_multixact | members_size_pretty
-----------+-------------+--------------+------------------+---------------------
 311740299 |  2785241176 |  13926205880 |                2 | 13 GB
</code></pre>

これにより、multixact のアクティビティを直接可視化できるようになります。`num_mxids` は現在使用されている multixact ID の数、`num_members` はそれらに含まれるエントリ数、`members_size` はそれらのメンバーが消費しているディスク容量を示します。`oldest_multixact` は、クラスターで依然として必要とされている最も古い multixact がどれだけ古いかを追跡するのに役立ちます。

`num_mxids` の急増は同時行ロックのワークロードを示し、`num_members` が増加しているにもかかわらず `oldest_multixact` が動かない場合は、長時間実行されているトランザクションがクリーンアップを妨げていることを示唆します。この関数には `pg_read_all_stats` が必要なため、モニタリング用ロールにそのまま組み込むことができます。

## ラップアラウンドまでの猶予期間を拡大 {#more_lead_time_before_wraparound}

最後に、トランザクション ID と multixact ID の両方について、ラップアラウンドの警告閾値がラップアラウンド発生前の 4,000 万トランザクションから 1 億トランザクションへと引き上げられました。4,000 万という数値は、トランザクションレートがはるかに低かった時代のものでした。毎秒 10,000 件の XID を消費するシステムでは約1時間でこの閾値に達してしまい、問題に気づいて対応し、負荷の高いバキュームを完了させるための猶予としては不十分でした。

重要な点として、これによってラップアラウンドが発生するタイミング自体が変わるわけではありません。単に Postgres がより早い段階から警告を発し始めるようになるだけです。`age(datfrozenxid)` に対するアラートを従来の警告ポイントに基づいた閾値で設定している場合は、見直しを行ってください。

## ツールへの影響:アップグレードチェックリスト {#what_this_means_for_tooling_an_upgrade_checklist}

内容が盛りだくさんでしたので、この記事の最後に対応が必要な項目のチェックリストをまとめました。オブザーバビリティツールを保守している方は、アップグレード前にこれらを確認してください(または、手元のエージェントにこのリストを渡してください 🙂)。

***免責事項:** このチェックリストは私にとっても机上の空論ではありません。私たちは Postgres から ClickHouse へクエリ統計をストリーミングするオープンソースの拡張機能である [pg_stat_ch](https://github.com/ClickHouse/pg_stat_ch) を保守しており、PostgreSQL 19 への対応は、まさに新しい WAL フィールドや新しい待機イベントといったこのリストを順番に確認していく作業そのものだからです。*

**ログパーサー**

* VACUUM/ANALYZE のログ行のパターンを更新してください。フィールドが追加されています:`... bytes, N full page image bytes, N buffers full`。
* autovacuum のログを読み取る処理に `log_autoanalyze_min_duration` を追加してください。`log_autovacuum_min_duration` 単体では ANALYZE の実行がレポートされなくなります。
* 新しいエントリタイプを想定してください:
  * これまで記録されなかったクラスターにおける `still waiting for lock` メッセージ
  * `received message via replication` / `received message via remote connection` エントリ(後者は常にローカルの `LOG` 重要度となるため、重要度ベースのフィルターではリモートの WARNING を捕捉できません)

**メトリクスコレクター**

* `pg_stat_wal` から `wal_fpi_bytes` の収集を開始し、`wal_bytes` に対する比率を追跡してください。
* クエリに `pg_get_multixact_stats()` を追加してください。モニタリングロールには `pg_read_all_stats` が必要です。この権限がない場合、関数はエラーを出さず NULL を返すため、メトリクスが警告なしに空になります。


**待機イベントディクショナリ**

* `CopyFromRead`、`CopyToWrite`、`WaitForWalWrite` を追加してください。`WaitForWalFlush` がスタンバイ上でも現れるようになった点にも注意してください。


**設定のバリデーションと UI**

* `log_min_messages` の新しい `type:level` リスト構文を受け付けるようにし、対応する autovacuum 項目の横に `log_autoanalyze_min_duration` を表示してください。

**アラート**

* サーバーが警告を出し始める前にアラートを発報できるよう、新しい 1 億の警告ポイントに合わせて `age(datfrozenxid)` のアラート閾値を再調整してください。

PostgreSQL 19 では、新しいシステムビュー(`pg_stat_lock`、`pg_stat_recovery`、`pg_stat_autovacuum_scores`)も導入され、既存のいくつかのビューも改善されています。これらについては別のブログ記事で取り上げる予定ですが、今のうちに確認しておくのもよいでしょう。

## おわりに {#outro}

個人的な話をすると、オブザーバビリティは私の Postgres に関する世界と日々の仕事が交差する領域です。[ClickHouse](https://clickhouse.com/) では [Postgres のマネージドサービス](https://clickhouse.com/cloud/postgres)を開発しており、ClickHouse 自体も Postgres などのシステムが生成するログ、メトリクス、トレースを保存・分析するために多くのエンジニアリングチームによって利用されています。そのため、シグナルを発行するデータベース側と、それらを保存して分析するデータベース側の両面からオブザーバビリティについて考える機会に恵まれています。

これらについて話をしてみたい方は、10月20日〜23日にバレンシアで開催される [PGConf.EU](http://PGConf.EU) でぜひお声がけください！

---

## ClickHouse Managed Postgres を今すぐ始める

ClickHouse Managed Postgres を実際のデータで試してみませんか？ClickHouse Cloud は数分で使い始めることができ、300 ドル分の無料クレジットも進呈されます。

[サインアップ](https://console.clickhouse.cloud/signUp?intent=pg&loc=blog-cta-2365-clickhouse-managed-postgres&utm_blogctaid=2365)

---