変更要約: 初版(主題G1・G1.1〜G1.4)
1.3データベースの構造
クラスタの物理的な実体とプロセスの内部構造を深掘りします。物理ファイル配置とプロセスアーキテクチャ(postmaster・backend・backgroundプロセス)、大きな値を分割格納するTOAST、テーブルの空き領域率を制御するFILLFACTOR、autovacuumの内部動作、外部データを扱う外部テーブル(FDW)のpostgres_fdw・file_fdw・CREATE SERVER・USER MAPPING・FOREIGN TABLEを押さえます。
ここまでの節でコマンドを「使う」側を学びました。この節ではその裏側、PostgreSQL が内部でどう動いているかに踏み込みます。なぜ大きな JSON 列がテーブルの外に格納されるのか、なぜ1接続ごとにプロセスが1つ立つのか、なぜ他DBのテーブルをあたかも自分のテーブルのように JOIN できるのか——構造を知ることで、性能問題や障害の根本原因に到達しやすくなります。
1.3.1物理配置とプロセスアーキテクチャ
- PostgreSQL はマルチプロセスアーキテクチャを採る(マルチスレッドではない)。postmaster=クラスタ起動時に最初に立ち上がる親プロセスで、接続要求を受け付けて子プロセスをフォークする司令塔。自らはクエリを処理しない。
- backend プロセス(
postgresプロセス)=クライアント1接続につき postmaster が1つフォークする、実際にクエリを実行するプロセス。接続がプロセスに1対1で対応するため、大量接続にはmax_connectionsの上限とメモリ消費への配慮が要る。 - backgroundプロセス=常駐する補助プロセス群。代表例=
checkpointer(チェックポイント実行)・background writer(ダーティページの平準化書き込み)・walwriter(WALバッファのディスク書き込み)・autovacuum launcher(autovacuumワーカーの起動)・stats collector/logical replication launcher。
1.3.2TOASTとFILLFACTOR
- TOAST(The Oversized-Attribute Storage Technique)=1ページ(既定8KB)に収まらない大きな値(長いテキスト・JSONB等)を、本体テーブルとは別のTOASTテーブル(
pg_toastスキーマ)に分割・場合により圧縮して格納する仕組み。本体行には参照ポインタだけが残るため、行自体は小さく保たれる。 - FILLFACTOR=テーブル作成時にページ内へ意図的に空き領域を残す割合(デフォルト100=目一杯詰める)。
CREATE TABLE t (...) WITH (fillfactor = 70);のように指定し、更新が多いテーブルで同一ページ内に新バージョンの行を置くHOT(Heap-Only Tuple)更新を成立させやすくし、インデックス更新の削減につながる。 - autovacuum の内部動作=
autovacuum launcherが定期的に各テーブルの肥大化度をチェックし、しきい値超過テーブルに対してautovacuum workerをフォークして VACUUM/ANALYZE を実行させる。ワーカー数の上限はautovacuum_max_workers、1ワーカーあたりの負荷抑制はautovacuum_vacuum_cost_limit(コストベースディレイ)で制御する。
1.3.3外部テーブル(FDW)
- FDW(Foreign Data Wrapper)=外部のデータソースをあたかもローカルテーブルであるかのように SQL から扱う仕組み。代表拡張=postgres_fdw(別の PostgreSQL サーバへ接続)・file_fdw(CSV等のファイルを読む)。
- 設定の3段階=CREATE SERVER(接続先ホスト・ポート・dbnameを定義)→USER MAPPING(ローカルロールと接続先の認証情報を対応付け:
CREATE USER MAPPING FOR local_user SERVER remote_srv OPTIONS (user 'x', password 'y'))→FOREIGN TABLE(実際に問い合わせる外部テーブル定義:CREATE FOREIGN TABLE)。 - postgres_fdw は書き込み(INSERT/UPDATE/DELETE)にも対応し、ローカルテーブルとの JOIN も可能。ただし外部サーバとの通信が発生するため、実行計画上のコスト見積もりやプッシュダウン(条件をリモート側で評価させる最適化)の有無が性能に影響する。
「起動時の親=postmaster(自身はクエリ処理しない)」「接続ごとに1プロセス=backend」「大きな値の分割格納=TOAST」「意図的な空き領域=FILLFACTOR(HOT更新を助ける)」「FDW設定の順序=CREATE SERVER→USER MAPPING→CREATE FOREIGN TABLE」 の対応が最頻出です。postgres_fdw と file_fdw の対象(別PostgreSQL vs ファイル)の取り違えにも注意します。
実務でよくある「なぜこの列は遅いのか」という問い合わせから構造理解を辿ってみましょう。あるテーブルの JSONB 列に大きなペイロード(数十KB)を格納しているとします。8KBページに収まらないため、この値は自動的に TOAST の対象となり、pg_toast.pg_toast_<oid> という別テーブルに(必要なら圧縮されて)格納されます。本体行にはポインタしか残らないため、その列を SELECT に含めなければ本体テーブルのスキャンは軽いままですが、実際にその列を取得するクエリでは追加の TOAST テーブルアクセスが発生しコストが増えます。次に、頻繁に UPDATE されるテーブルでインデックスの更新コストが気になる場合、FILLFACTOR を下げる(例:70)ことで各ページに空きを残し、更新後の新バージョンが同じページ内に収まりやすくします。これが成立すると HOT 更新となり、対象列にインデックスが張られていない限りインデックスエントリの更新が省略され、書き込み性能が向上します。
プロセスの視点では、接続プールを使わずアプリケーションが大量の同時接続を張ると、backend プロセスが接続数だけフォークされ、max_connections の上限に達したり、メモリ消費(work_mem × 同時実行クエリ数)が想定を超えたりします。この状況を調査するには ps aux | grep postgres でバックエンドプロセスの数を数えたり、pg_stat_activity と突き合わせたりします。最後に、複数拠点のPostgreSQLをまたいだレポーティングが必要になった場面を考えます。CREATE EXTENSION postgres_fdw; の後、CREATE SERVER remote_srv FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host '10.0.0.5', dbname 'sales'); で接続先を定義し、CREATE USER MAPPING FOR report_user SERVER remote_srv OPTIONS (user 'ro_user', password '*'); でローカルロールと接続先ロールを対応付け、最後に CREATE FOREIGN TABLE remote_orders (...) SERVER remote_srv OPTIONS (table_name 'orders'); を定義すれば、ローカルの SELECT から remote_orders をあたかもローカルテーブルのように JOIN できるようになります。
| 要素 | 内容 | 備考 |
|---|---|---|
| postmaster | 起動時の親プロセス | 自身はクエリを処理しない |
| backend | 接続ごとのクエリ実行プロセス | 接続数だけフォークされる |
| TOAST | 大きな値の分割格納 | pg_toastスキーマに格納 |
| FILLFACTOR | 意図的な空き領域率 | 低くするとHOT更新を助ける |
| postgres_fdw | 外部PostgreSQL接続 | 書き込み・JOINにも対応 |
| file_fdw | 外部ファイル読込 | CSV等を読み取り専用で扱う |
ひっかけ: 「postmaster がクライアントの各クエリを直接処理する」は誤りです。postmaster は接続受付とプロセスのフォークのみを行い、実際のクエリ処理は backend プロセスが担当します。また「FILLFACTOR を100に近づけるほど更新性能が上がる」も誤り=FILLFACTORを下げて空きを残すことがHOT更新を促し更新性能を助けるのであり、100(目一杯詰める)はむしろ更新のたびにページをまたぐ可能性を高めます。
1.3.4この節のまとめ
- postmaster(親・フォークのみ)→backend(接続ごと・クエリ実行)+backgroundプロセス(checkpointer/background writer/walwriter/autovacuum launcher)というマルチプロセス構成
- TOAST=大きな値の分割格納/FILLFACTOR=意図的な空き領域(低いほどHOT更新を助ける)
- FDWはCREATE SERVER→USER MAPPING→FOREIGN TABLEの順。postgres_fdw=別PostgreSQL/file_fdw=外部ファイル
進捗の記録にはログインが必要です。
理解度チェック
(軽い確認用)Q1. PostgreSQLのプロセスアーキテクチャに関する説明として正しいのはどれ?
Q2. 更新の多いテーブルでインデックス更新コストを抑えたい。HOT(Heap-Only Tuple)更新が発生しやすくなるよう調整すべきパラメータはどれ?
Q3. 別のPostgreSQLサーバ上のテーブルをローカルからJOINできるようにしたい。postgres_fdwを使う設定の正しい順序はどれ?

