本番運用でつまずきやすい点を、原因→対策→確認コマンドの流れでまとめました。Aurora PostgreSQL でも同様に有効です。
1) パーティション数が多すぎる
症状: INSERT/プランニングが遅い、pg_class
が膨張、オブジェクト管理が地獄。
指針
- 日次切り: 直近 90–180 日程度に限定(古い分は月次へ再集約 or アーカイブ)
- 月次切り: 数年保管までなら現実的(数百パーティション規模が上限目安)
チェック
SELECT relname, relkind, reltuples, relpages
FROM pg_class WHERE relname LIKE 'inference_logs%';
2) パーティション・プルーニングが効いていない
症状: すべての子テーブルにアクセスし、想定より遅い。
原因
- WHERE 句がパーティションキーにマッチしていない(関数適用や型不一致)。
- プラン時間/実行時間のプルーニング条件に届かない。
対策
- パーティションキー(例:
created_at
)に 関数をかけない。date_trunc
は述語側でなく引数側で整える。 - バインド変数でも効くよう、PG11+の 実行時プルーニング を前提にする。
確認
EXPLAIN (ANALYZE, BUFFERS)
SELECT * FROM inference_logs
WHERE created_at >= TIMESTAMPTZ '2025-09-01' AND created_at < TIMESTAMPTZ '2025-10-01';
-- → 触っているパーティション数を確認
3) UNIQUE/PRIMARY KEY が作れない/効かない
症状: 親テーブルにユニーク制約を作れない、または“パーティション単位でしか”一意にならない。
背景
- グローバルインデックスがないため、一意制約はパーティションキーを必ず含める 必要がある。
対策例
-- OK: (id, created_at) を主キーにして created_at で RANGE 分割
ALTER TABLE inference_logs
ADD CONSTRAINT inference_logs_pkey PRIMARY KEY (id, created_at);
4) 親に作ったインデックスが子に反映されない
症状: パーティションごとに手でインデックスを作っていてツラい。
正解パターン
- 親に パーティション化インデックス を作成すると、以後作る子に自動適用される。
- 既存の子には
ATTACH PARTITION
で後付け可能。
例
-- 親に定義(以後の子に自動適用)
CREATE INDEX ON inference_logs (user_id, created_at);
-- 既存の子に後付け
CREATE INDEX ON inference_logs_2025_09 (user_id, created_at);
ALTER INDEX inference_logs_user_id_created_at ATTACH PARTITION inference_logs_2025_09_user_id_created_at_idx;
5) デフォルト・パーティションの落とし穴
症状: 境界外データが全部 DEFAULT
に溜まり、片肺運転に。
対策
DEFAULT
は 安全弁 として最小限に。定期的に再配分するジョブを用意。- 可能なら 先行作成(次月/翌週)を徹底し、
DEFAULT
に落ちないようにする。
例
CREATE TABLE inference_logs_default
PARTITION OF inference_logs DEFAULT;
6) 境界ミス(FROM/TO の半開区間)
症状: 9/30 の 23:59:59.999999+09:00 が想定外のパーティションに入る。
対策
- RANGE は [FROM, TO) の半開区間。月初・月初で揃える。
- タイムゾーンを明示(
TIMESTAMPTZ
)。
テンプレ
CREATE TABLE inference_logs_2025_09
PARTITION OF inference_logs
FOR VALUES FROM ('2025-09-01 00:00+09') TO ('2025-10-01 00:00+09');
7) 外部キー・参照整合のコスト
症状: 参照整合のチェックで遅い。
対策
- 参照先/参照元も同じキーで 共同パーティショニング(co-partition)する。
- 参照整合が不要なログ系は FK を設けない 設計も検討。
8) Autovacuum の“効きが悪い”
症状: 膨れたまま戻らない、統計が古い。
対策
- パーティション 単位で autovacuum 設定を上書き。
- 書き込みの多い区画は
autovacuum_vacuum_scale_factor
を小さめに。
例
ALTER TABLE inference_logs_2025_09 SET (
autovacuum_vacuum_scale_factor = 0.05,
autovacuum_analyze_scale_factor = 0.02
);
9) COPY/バルク投入が遅い
原因: ルーティング・トリガのオーバーヘッド。
対策
- 可能なら 単一パーティションに対して直接 COPY。
- 期間ごとにファイルを分け、対応する子へ投入。
例
\copy inference_logs_2025_09 (created_at, user_id, payload) FROM '2025-09.csv' CSV;
10) パーティション間の再配置・再圧縮
ユースケース: ホット (直近) は日次、コールドは月次へ寄せて台数削減。
パターン
- 新しい集約先(月次)を作る
INSERT ... SELECT
で移送(またはCREATE TABLE AS
)- 旧日次を
DETACH PARTITION
→DROP
or アーカイブ
11) パーティションワイズ・ジョイン/集計を生かす
ポイント
- 設定:
enable_partitionwise_join = on
,enable_partitionwise_aggregate = on
- 同じキーで分割したテーブル同士は、子同士で並列に処理できる。
確認
SHOW enable_partitionwise_join;
SHOW enable_partitionwise_aggregate;
12) 誤ったキー選定
アンチパターン
- 一様分布しないキー(例:
model_version
が数種類しかない)で RANGE 分割 → 偏り。 user_id
のみで RANGE 分割 → 範囲クエリ(時間絞り)が効かない。
推奨
- 時系列ワークロードは 時間 + サブキー(LIST/HASH 併用も検討)。
13) 監視を忘れる
最低限のメトリクス
- パーティション数、最古/最新の境界、
DEFAULT
の件数 - オートバキューム実行回数・所要時間、bloat 推定
- 主要クエリの触れたパーティション数(
pg_stat_statements
+EXPLAIN
サンプリング)
運用レシピ
A. 先行パーティション自動生成(例: 次月分を毎月25日に)
DO $$
DECLARE
y int := EXTRACT(YEAR FROM now());
m int := EXTRACT(MONTH FROM now());
next_first date := (date_trunc('month', now()) + interval '1 month')::date;
next_next_first date := (date_trunc('month', now()) + interval '2 month')::date;
part_name text := format('inference_logs_%s_%s', to_char(next_first,'YYYY'), to_char(next_first,'MM'));
BEGIN
IF NOT EXISTS (
SELECT 1 FROM pg_class WHERE relname = part_name
) THEN
EXECUTE format($$CREATE TABLE %I PARTITION OF inference_logs
FOR VALUES FROM (%L) TO (%L)$$,
part_name, next_first, next_next_first);
END IF;
END$$;
B. プルーニングが効くかワンライナーチェック
EXPLAIN SELECT 1 FROM inference_logs
WHERE created_at >= '2025-09-01' AND created_at < '2025-10-01';
-- → "Partitioned Seq Scan" の "Partitions scanned" が少数か確認
C. アーカイブ&DROP(安全手順)
- 対象パーティションを 読み取り専用 に(任意)
- 外部へエクスポート(S3 など)
- 参照が無いことを確認
ALTER TABLE ... DETACH PARTITION ...;
→ 必要なら DROP
まとめ(運用の勘所)
“自動化する”: 先行作成・監視・アーカイブのジョブ化で人手依存を排除。長期運用でも安定したログ処理基盤を維持できます。
“先に決める”: キー選定と保持方針(ホット/コールド)を最初に。
“測って直す”: EXPLAIN と実測でプルーニング確認、設定は子ごと最適化。
“増やしすぎない”: パーティション数は管理できる上限に抑える。