PostgreSQL宣言的テーブルパーティショニング構築とプルーニング確認
PostgreSQL Database Performance SQL
結論
PARTITION BY RANGE で親を作成し、検索条件ではパーティションキーに関数を適用せず生の範囲指定を行います。
-- PostgreSQL公式仕様:月別宣言的パーティショニングの作成
-- 1. 親テーブル作成
CREATE TABLE logs (
id bigserial,
log_level text,
created_at timestamptz not null,
primary key (id, created_at)
) PARTITION BY RANGE (created_at);
-- 2. 2026年7月用の子テーブル作成
CREATE TABLE logs_y2026m07 PARTITION OF logs
FOR VALUES FROM ('2026-07-01 00:00:00+00') TO ('2026-08-01 00:00:00+00');
-- 3. 範囲外データのクラッシュを防ぐ DEFAULT パーティション
CREATE TABLE logs_default PARTITION OF logs DEFAULT;
PostgreSQL公式仕様:Partition Pruning(間引き)の仕組み
PostgreSQL公式ドキュメント(postgresql.org/docs/current/ddl-partitioning.html)の規定通り、宣言的パーティショニングを設定したテーブルに対して検索クエリを発行すると、オプティマイザは Partition Pruning (パーティション間引き) 機能を起動します。
のような検索条件がある場合、2026年7月以外の全子テーブルの検索を計画段階および実行段階で完全削除(Skip)し、極小の対象子テーブルのみを高速スキャンします。
実際に起こる障害:WHERE 句の関数適用による全子テーブルフルスキャン
Pruning 機能を無効化させてしまう開発上の落とし穴が、WHERE 句でパーティションキー(created_at)を関数で包んでしまうパターン です。
-- ❌ 事故:Pruning が無効化され、過去数年分の全子テーブルを走査するダメなクエリ
SELECT * FROM logs
WHERE date_trunc('month', created_at) = '2026-07-01'::timestamptz;
-- ⭕ 正解:生のカラムに対して範囲演算子を指定する(Pruning が正常動作)
SELECT * FROM logs
WHERE created_at >= '2026-07-01 00:00:00+00'
AND created_at < '2026-08-01 00:00:00+00';
関数を適用すると、オプティマイザが範囲境界の計算を行えず、数十〜数百個存在する全パーティション子テーブルに対して全件スキャンが走り、大規模障害を引き起こします。
構築手順
- 親テーブルを
PARTITION BY RANGE (created_at)で作成し、主キーには必ずパーティションキーを含める - 月別または年別の子テーブルを
FOR VALUES FROM (...) TO (...)で定義する - 定義範囲外データによる
ERROR: no partition of relationクラッシュを防ぐためDEFAULTパーティションを併設する EXPLAINでSubplans Removedが表示され、目的の子テーブルのみが走査されているか検証する