PostgreSQL 18 移行でつまずく原因と対処法

目次
  1. 結論:PostgreSQL 18 でまず確認する3点と切り分け手順
  2. pg_upgrade が失敗・中断するときの対処
  3. アプリから接続できない・認証エラーになるときの対処
  4. アップグレード後に遅い・実行計画が変わったときの対処
  5. よくある質問
  6. 再発防止:移行前チェックリストとさらに学ぶには
このテーマのまとめPostgreSQLが遅い・詰まる原因|VACUUMと実行計画、ロック
本記事にはプロモーション(広告)が含まれています。

PostgreSQL 18は2025年9月にリリースされたメジャーバージョンです。initdbの既定値変更、非同期I/Oの導入、pg_upgradeによる統計情報の引き継ぎなど、移行時の挙動に直接影響する変更が複数入りました。結果として「17まで通っていた手順がそのまま通らない」「上がったのに遅い」という詰まりが起きやすくなっています。

ここでは移行直後に出やすい症状を「pg_upgradeが失敗する」「接続できない」「遅くなった」の3つに分け、確認コマンドと判断基準を整理します。既定値や制限値はマイナーリリースで調整されるため、最終判断は使用中バージョンの公式ドキュメント(リリースノートと各GUCの項目)で確認してください。

結論:PostgreSQL 18 でまず確認する3点と切り分け手順

最初に見るのは、18で本当に動いているかとチェックサム設定が旧クラスタと一致しているかというクラスタの物理状態、pg_upgradeの出力ディレクトリとサーバログのFATAL/WARNING行、そして変更された既定値(data_checksums、password_encryption、io_method、effective_io_concurrency)の3点です。ここで当たりを付けずに設定を触ると、かえって状態を悪化させます。

症状別の切り分け順は次の通りです。

  • pg_upgradeが完走しない → チェックサム不一致 → 拡張ライブラリ不足 → ロケール/エンコーディング → ディスク容量
  • 接続できない → pg_hba.confの認証方式 → ロールのパスワード検証子の形式 → クライアントドライバのSCRAM対応
  • 遅い → 統計情報の有無 → 実行計画の差分 → I/O関連パラメータ

稼働中のバージョンとクラスタ状態を確認する(version / pg_controldata)

SELECT version();
SHOW server_version_num;   -- 18 系は 180000 以上
SHOW data_checksums;       -- on / off
SHOW password_encryption;
SHOW io_method;

接続できない状況なら、データディレクトリから直接読みます。pg_controldataは停止中のクラスタでも実行できます。

/usr/pgsql-18/bin/pg_controldata -D /var/lib/pgsql/18/data | \
  egrep 'version|checksum|state|Latest checkpoint location'

見るポイントは3行です。

  • Database cluster stateがshut downかin productionか(pg_upgradeとpg_checksumsは正常停止済みでなければ処理を拒否します)
  • Data page checksum version(0=無効、1=有効)
  • Catalog version number / pg_control version number(バイナリとデータディレクトリの世代一致)

サーバログと pg_upgrade のログから失敗箇所を特定する

pg_upgradeは実行したカレントディレクトリにpg_upgrade_output.d/<タイムスタンプ>/を作り、その下にログと診断ファイルを残します。失敗したらまずここを見てください。

ls -R pg_upgrade_output.d/
# log/pg_upgrade_internal.log … 進行ログと終了理由
# log/pg_upgrade_server.log   … 一時起動したサーバの出力
# log/pg_upgrade_dump_*.log   … スキーマ移行の SQL エラー
# loadable_libraries.txt      … 新クラスタに無い共有ライブラリ一覧

成功すると出力ディレクトリは削除されるため、残っていること自体が途中で止まった目印になります。

サーバログ側では、18でlog_connectionsが真偽値から記録項目のリスト指定に変わりました。認証系の切り分けでは、接続の受付と認証の両方を出力させます。

# postgresql.conf(例)
log_connections = 'receipt,authentication'
log_line_prefix = '%m [%p] %q%u@%d %h '
log_min_messages = info

従来のon/offも受け付けますが、意味の対応は公式ドキュメントで確認してから設定してください。変更後はSELECT pg_reload_conf();で反映されます。

pg_upgrade が失敗・中断するときの対処

本番前に--checkを通します。データを変更せず整合性チェックだけを行うモードです。

/usr/pgsql-18/bin/pg_upgrade \
  -b /usr/pgsql-16/bin -B /usr/pgsql-18/bin \
  -d /var/lib/pgsql/16/data -D /var/lib/pgsql/18/data \
  --check

データチェックサムの不一致で止まるケース

18で最も遭遇しやすいのがこれです。18のinitdbはデータチェックサムを既定で有効化します。一方、16以前で既定値のまま作ったクラスタはチェックサム無効です。設定が食い違うと、pg_upgradeは次のような趣旨のエラーで停止します。

old cluster does not use data checksums but the new one does

対処は2通り。どちらを選ぶかは停止許容時間で決めます。

(A) 新クラスタ側をチェックサム無効で作り直す(停止時間を増やさない)

/usr/pgsql-18/bin/initdb -D /var/lib/pgsql/18/data --no-data-checksums \
  --encoding=UTF8 --locale=ja_JP.UTF-8

(B) 旧クラスタ側のチェックサムを有効化してから上げる(推奨だが停止時間が伸びる)

pg_ctl -D /var/lib/pgsql/16/data stop -m fast     # 正常停止が必須
/usr/pgsql-16/bin/pg_checksums -D /var/lib/pgsql/16/data --enable --progress
/usr/pgsql-16/bin/pg_controldata -D /var/lib/pgsql/16/data | grep checksum

pg_checksums --enableは全データページを書き換えるため、所要時間はデータ量とストレージ帯域にほぼ比例します。数百GB規模ではメンテナンス枠に収まらないこともあるので、同サイズの検証環境で事前に計測してください。チェックサムは読み取り時にページ破損を検知する仕組みで、ハードウェア起因の破損をエラーなしに見逃す事態を防げます。恒久的には(B)を原則とし、時間が取れない場合は(A)で先に上げて後日オフラインで有効化します。

チェックサムの有効/無効はクラスタ単位で、ストリーミングレプリケーションの待機系も同じ状態にそろえる必要があります。待機系は切り替え後にpg_basebackupで再構築するのが確実です。

拡張機能・ロケール・ディスク容量が原因のケース

loadable_libraries.txtに名前が出た拡張機能は、18向けのパッケージ(postgis、pgaudit、pg_repackなど)を先に入れます。移行前に一覧を取っておきましょう。

SELECT extname, extversion FROM pg_extension ORDER BY 1;
SELECT name, default_version, installed_version
  FROM pg_available_extensions
 WHERE installed_version IS NOT NULL;

移行後はALTER EXTENSION <name> UPDATE;で18向けのバージョンに合わせます。忘れると関数定義が古いまま残り、想定外のエラーが発生します。

ロケールとエンコーディングについては、pg_upgradeが旧クラスタと新クラスタのエンコーディング、LC_COLLATE/LC_CTYPE、ロケールプロバイダ(libc / icu / builtin)の一致を要求します。initdbの引数で明示的に揃えるのが安全です。

SELECT datname, pg_encoding_to_char(encoding), datcollate, datctype,
       datlocprovider, datcollversion
  FROM pg_database;

OS更新を伴う移行では、ICUやglibcの照合順序バージョンが変わりcollation version mismatchの警告が出ることがあります。この場合、文字列カラムのB-treeインデックスが保持する並び順と実際の照合順序がずれるため、該当インデックスのREINDEX(またはREINDEX DATABASE)が必要です。放置すると、存在する行が検索で返らないという発見しにくい不具合になります。

ディスク容量は、既定の--copyモードで旧クラスタと同容量の空きが必要です。同一ファイルシステム上なら--link(ハードリンク)でほぼ瞬時に移行できますが、成功後に旧クラスタを起動するとデータが破損するため、ロールバックはバックアップからの復旧に限られます。並列化は--jobsで指定します。

df -h /var/lib/pgsql
pg_upgrade ... --link --jobs=4

この記事のテーマをさらに学べる本

PostgreSQLの内部構造や認証、レプリケーション、モニタリング、性能改善を体系的に扱っており、移行後の運用判断の土台になります。

PostgreSQL実践入門──アーキテクチャ、運用監視、性能改善
PostgreSQL実践入門──アーキテクチャ、運用監視、性能改善堀口 恭太郎/細谷 柚子/渡 佑也/山田 達朗/白石 裕輝/須賀 啓敏
楽天ブックスで詳しく見る Yahoo!ショッピングで見る

アプリから接続できない・認証エラーになるときの対処

18で接続できない場合、原因はほぼpg_hba.confの認証方式かパスワード検証子の形式です。症状とログの文言で切り分けます。

  • FATAL: password authentication failed for user "app" → 検証子の形式かパスワード自体
  • FATAL: no pg_hba.conf entry for host ... → ホスト/データベース/ユーザの行が無い
  • クライアント側のauthentication method 10 not supported系 → ドライバがSCRAM未対応

md5 から scram-sha-256 へ切り替える手順と接続テスト

password_encryptionの既定値は14以降scram-sha-256です。古い設定ファイルを引き継いだ環境では、サーバがscram-sha-256でハッシュを作る一方、ロールにはmd5形式の検証子が残る混在状態になりがちです。現状はこう確認します。

SELECT rolname,
       CASE WHEN rolpassword LIKE 'SCRAM-SHA-256$%' THEN 'scram'
            WHEN rolpassword LIKE 'md5%'            THEN 'md5'
            WHEN rolpassword IS NULL                THEN 'none'
            ELSE 'other' END AS kind
  FROM pg_authid ORDER BY 1;   -- スーパーユーザで実行

切り替えは次の順番で行ってください。逆順にするとロックアウトします。

-- 1) サーバ側の既定を確認・設定
ALTER SYSTEM SET password_encryption = 'scram-sha-256';
SELECT pg_reload_conf();

-- 2) 各ロールのパスワードを再設定して検証子を作り直す
--    (同じ平文を入れ直してもハッシュ形式は更新される)
ALTER ROLE app PASSWORD 'xxxxxxxx';
# 3) pg_hba.conf の該当行を書き換える
host    all    app    10.0.0.0/24    scram-sha-256
# 4) リロードして接続テスト
psql -h 127.0.0.1 -U postgres -c "SELECT pg_reload_conf();"
PGPASSWORD='xxxxxxxx' psql "host=10.0.0.10 user=app dbname=appdb sslmode=require" -c "select current_user, ssl_is_used()"

接続元の整合性はpg_hba_file_rulesで先に確認できます。構文エラーや意図しない行の先マッチを、リロード前に検出できます。

SELECT rule_number, type, database, user_name, address, auth_method, error
  FROM pg_hba_file_rules ORDER BY rule_number;

クライアント側では、SCRAMがlibpq 10以降、JDBCは42.2以降で対応しています。アプリサーバのOSに古いlibpq(PostgreSQL 9.x系のクライアントパッケージ)が残っていると、サーバを18に上げた直後に認証段階で失敗します。psql --versionやlddで実際にリンクされているライブラリを確認してください。

認証方式を一度に切り替えられない場合は、ロール単位・接続元単位でpg_hba.confの行を分け、移行済みのロールから順にscram-sha-256へ寄せるのが現実的です。

アップグレード後に遅い・実行計画が変わったときの対処

「上がったが遅い」の原因は、統計情報の欠落、実行計画の変化、I/O設定の既定値変更に分かれます。まず統計から見ます。

統計情報が引き継がれたか確認し、不足分だけ ANALYZE する

17以前はpg_upgrade後にオプティマイザ統計が空になるため、全体ANALYZEが事実上必須でした。18ではpg_upgradeがオプティマイザ統計を引き継ぎます(意図的に捨てる場合は--no-statistics)。ただしCREATE STATISTICSで作った拡張統計など引き継がれない種類もあるので、全部入っている前提は置かないでください。

確認にはpg_statsとpg_classを使います。累積統計ビュー(pg_stat_*のカウンタ)は引き継がれないため、pg_stat_user_tables.last_analyzeはNULLのままで判断材料になりません。

-- 列統計が入っていないテーブルを洗い出す
SELECT c.relname
  FROM pg_class c
  JOIN pg_namespace n ON n.oid = c.relnamespace
 WHERE c.relkind IN ('r','m','p')
   AND n.nspname NOT IN ('pg_catalog','information_schema')
   AND NOT EXISTS (SELECT 1 FROM pg_statistic s WHERE s.starelid = c.oid)
 ORDER BY 1;

-- 拡張統計の状態
SELECT statistics_name, n_distinct, dependencies FROM pg_stats_ext;

不足分だけ埋めるには、18で追加された--missing-stats-onlyが有効です。移行直後の全体ANALYZEによる負荷を避けられます。

/usr/pgsql-18/bin/vacuumdb --all --analyze-in-stages --missing-stats-only

--analyze-in-stagesは精度の低いサンプルで素早く一巡し、段階的に精度を上げます。サービス再開を急ぐ場面に向く動作です。落ち着いたら通常のANALYZE、またはautovacuumの自然な実行に任せます。

実行計画が変わった場合は、18でEXPLAIN ANALYZEが既定でBUFFERSを出力する点を活用し、推定行数と実測行数のズレ、共有バッファのヒット状況を比較します。

EXPLAIN (ANALYZE, SETTINGS) SELECT ...;

読み方はPostgreSQL EXPLAIN ANALYZEの見方と同じ手順で、推定と実測の乖離が最初に大きくなるノードを探します。18では複数列B-treeインデックスの先頭列をスキップして使えるスキップスキャン、GINの並列構築などプランナの選択肢が増えたため、インデックス構成を変えていないのに計画だけが変わることがあります。単発の遅延ならプランの差分を取り、恒常的に遅いなら統計の鮮度とwork_memを先に見てください。

非同期I/O(io_method)とIOワーカーの設定を確認する

18では読み取り系のI/Oに非同期化の仕組みが入り、io_methodパラメータが追加されました。取り得る値はsync(従来相当)、worker(既定)、io_uring(Linuxかつliburing付きでビルドされた場合)です。

SHOW io_method;
SHOW io_workers;
SHOW effective_io_concurrency;
SHOW maintenance_io_concurrency;

性能劣化が疑わしいときは、比較のため一時的にio_method = syncにして再起動し、差が出るかを確認します。io_methodの変更には再起動が必要です。workerのままI/Oが伸びない場合はio_workers(既定3)の不足を疑います。並列度の高い読み取りで、CPUは空いているのに読み取りスループットが上がらないケースが該当します。増やしすぎるとワーカー自体がオーバーヘッドになるため、1〜2段階ずつ変えて計測してください。

io_uringで起動しない原因としては、カーネル、ulimit -l(メモリロック上限)、ファイルディスクリプタ上限が考えられます。起動ログにio_uring初期化失敗の行が出ていないか確認します。effective_io_concurrencyの既定値が18で引き上げられた点も見てください。NVMeなら妥当でも、帯域の細い共有ストレージでは過剰になることがあります。

効果の確認にはpg_stat_ioを使います。reads/read_bytes/read_timeをbackend_typeとcontext(normal、vacuum、bulkreadなど)で分けて見ると、どの経路が非同期化の恩恵を受けているか判別できます。

SELECT backend_type, object, context, reads, read_bytes, read_time
  FROM pg_stat_io
 WHERE reads > 0
 ORDER BY read_time DESC NULLS LAST LIMIT 20;

SELECT * FROM pg_aios;   -- 進行中の非同期I/O

マネージドサービスではio_method系のパラメータが公開されていない、または既定値が異なる場合があります。RDS / Auroraでの可用性はパラメータグループの一覧で確認してください。Auroraは独自のストレージ層を持つため、調整ポイント自体が異なります(Aurora/RDS PostgreSQLの違いと性能差の原因)。

OSS-DB Silver 対策テキスト(OSS教科書)

PostgreSQLの仕組みを体系的に学ぶなら、OSS-DB Silverの公式対応テキストが近道です。資格を取らなくても基礎固めに役立ちます。

OSS教科書 OSS-DB Silver Ver.3.0対応 (EXAMPRESS) [ 福岡 博 ]

OSS教科書 OSS-DB Silver Ver.3.0対応 (EXAMPRESS) [ 福岡 博 ]

よくある質問

PostgreSQL 18 で md5 認証はまだ使える?

使えますが非推奨になりました。設定時や使用時に警告が記録され、将来のメジャーバージョンで削除される方向が示されています。新規環境で選ぶ理由はなく、既存環境も計画的にscram-sha-256へ移す前提で考えてください。

pg_hba.confにmd5と書いてあっても、そのロールのパスワードがSCRAM形式で保存されていればサーバはSCRAMで認証します。逆にscram-sha-256と書いた行に対して、md5形式の検証子しか持たないロールは認証に失敗します。この非対称性を押さえておくと切り分けが速くなります。

データチェックサムは稼働後から有効化できる?

できますが、18時点ではクラスタを正常停止した状態でpg_checksums --enableを実行する方式のみです。オンラインでの有効化はできません。全ページを書き換えるので所要時間はデータ量に比例し、実行中はクラスタを起動できません。結果はSHOW data_checksumsまたはpg_controldataのData page checksum versionで確認します。レプリケーション構成では各ノードで個別に実施するか、プライマリで有効化してから待機系を再構築してください。

旧バージョンのクライアントやドライバはそのまま接続できる?

フロントエンド/バックエンドプロトコルの互換性は保たれており、古いクライアントは下位のプロトコルバージョンで接続できます(18では新しいマイナープロトコルと、libpq側で上限を指定するパラメータが追加されています)。実務で問題になるのはプロトコルより、SCRAM非対応の古いlibpqやドライバで認証段階が失敗する点、そしてバックアップ・移行ツールのバージョンです。pg_dump/pg_restore/pg_dumpallは新しい側のバージョンを使うのが原則で、18のサーバを16のpg_dumpでダンプするとエラーや不完全な出力になり得ます。

再発防止:移行前チェックリストとさらに学ぶには

次のチェックを移行前に済ませておくと、当日に判断を迫られる項目がほぼなくなります。

  • pg_upgrade --checkが警告なしで通る(本番と同じデータ量の複製で)
  • 旧クラスタと新クラスタのdata_checksumsが一致している
  • loadable_libraries.txtが空/18向け拡張パッケージを導入済み
  • エンコーディング・LC_COLLATE・ロケールプロバイダが一致、ICU/glibc変更時のREINDEX計画あり
  • 全ロールのパスワード検証子がscram-sha-256形式、アプリ側ドライバのバージョン確認済み
  • pg_hba_file_rulesでエラー行なし
  • --copyなら旧クラスタ相当の空き容量、--linkならバックアップからの復旧手順を文書化
  • 論理レプリケーションスロットとサブスクリプションの状態を把握(未消費スロットが残るとWALが溜まります。詳細はPostgreSQL WAL肥大化の原因|レプリケーションスロット)
  • 移行後にvacuumdb --all --analyze-in-stages --missing-stats-onlyを流す手順を組み込み済み
  • 主要クエリのEXPLAIN (ANALYZE, BUFFERS)を移行前に採取し、比較できる状態

18の変更点は「既定値が変わった」「引き継がれる範囲が広がった」「I/O経路が増えた」に整理できます。どれも仕組みを知っていれば確認コマンド1つで判別が付く一方、知らないと切り分けに時間を取られます。WALとチェックポイント、MVCCとVACUUM、プランナのコスト計算といった内部構造を押さえておくのが、トラブル対応の精度を上げる近道です。