postgres-optimization について
クエリ、インデックス、データベース操作を調整する開発者向けのPostgreSQL最適化スキルです。実行計画の分析、パーティショニング、JSONB、コネクションプーリング、一般的な調整項目を扱います。
主な機能
- 01クエリパターンに応じたインデックス戦略
- 02EXPLAIN実行計画の分析
- 03テーブル分割の指針
- 04JSONBの検索と更新例
- 05コネクションプーリングの設定
- 06性能と未使用インデックスの確認
活用シーン
- 01PostgreSQLの遅いクエリの調査
- 02頻出する絞り込みやソート向けのインデックス選定
- 03パーティショニングによる大規模テーブルの処理
- 04Webアプリケーションのコネクションプーリング設定
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ rohitg00/ awesome-claude-code-toolkit --skill postgres-optimizationインストール後、Claude Code で/postgres-optimizationを入力して読み込まれたか確認します。
詳細
PostgreSQL の最適化
インデックス戦略
-- B-tree index for equality and range queries (default)
CREATE INDEX idx_orders_customer_id ON orders (customer_id);
-- Composite index (column order matters: equality columns first, range last)
CREATE INDEX idx_orders_status_created ON orders (status, created_at DESC);
-- Partial index (smaller, faster for filtered queries)
CREATE INDEX idx_orders_pending ON orders (created_at)
WHERE status = 'pending';
-- Covering index (avoids table lookup entirely)
CREATE INDEX idx_users_email_name ON users (email) INCLUDE (name, avatar_url);
-- GIN index for JSONB containment queries
CREATE INDEX idx_products_metadata ON products USING GIN (metadata);
-- GiST index for full-text search
CREATE INDEX idx_articles_search ON articles USING GiST (
to_tsvector('english', title || ' ' || body)
);
-- Concurrent index creation (no table lock)
CREATE INDEX CONCURRENTLY idx_large_table_col ON large_table (col);
クエリプランの読み方
EXPLAIN (ANALYZE, BUFFERS, FORMAT TEXT)
SELECT o.id, o.total, u.name
FROM orders o
JOIN users u ON o.user_id = u.id
WHERE o.status = 'shipped'
AND o.created_at > NOW() - INTERVAL '30 days'
ORDER BY o.created_at DESC
LIMIT 20;
プランで確認すべき主なポイント:
- 大きなテーブルでの
Seq Scanは、インデックスが不足していることを示します - 推定行数が多い
Nested Loopは、結合インデックスが不足している可能性を示します Index Scanを伴わないSortは、ソートがメモリまたはディスク上で行われていることを意味しますBuffers: shared hitとshared readの比較から、キャッシュ効率がわかります
パーティショニング
CREATE TABLE events (
id BIGINT GENERATED ALWAYS AS IDENTITY,
event_type TEXT NOT NULL,
payload JSONB NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
) PARTITION BY RANGE (created_at);
CREATE TABLE events_2024_q1 PARTITION OF events
FOR VALUES FROM ('2024-01-01') TO ('2024-04-01');
CREATE TABLE events_2024_q2 PARTITION OF events
FOR VALUES FROM ('2024-04-01') TO ('2024-07-01');
-- Index on each partition (inherited automatically in PG 11+)
CREATE INDEX ON events (created_at, event_type);
クエリでパーティションキーによる絞り込みが常に行われる場合は、1,000 万行を超えるテーブルをパーティション分割します。
JSONB 操作
-- Query nested JSONB fields
SELECT * FROM products
WHERE metadata @> '{"category": "electronics"}'
AND (metadata ->> 'price')::numeric < 500;
-- Update nested JSONB
UPDATE products
SET metadata = jsonb_set(metadata, '{stock}', to_jsonb(stock - 1))
WHERE id = 'abc';
-- Aggregate JSONB arrays
SELECT id, jsonb_array_elements_text(metadata -> 'tags') AS tag
FROM products
WHERE metadata ? 'tags';
コネクションプーリング
# pgbouncer.ini
[databases]
app = host=localhost port=5432 dbname=app
[pgbouncer]
pool_mode = transaction
max_client_conn = 1000
default_pool_size = 25
min_pool_size = 5
reserve_pool_size = 5
server_idle_timeout = 300
Web アプリケーションではトランザクションレベルのプーリングを使用します。プリペアドステートメントまたは一時テーブルを使用するアプリケーションでは、セッションレベルのプーリングを使用します。
よく使われるチューニングパラメーター
-- Check for slow queries
SELECT query, calls, mean_exec_time, total_exec_time
FROM pg_stat_statements
ORDER BY total_exec_time DESC
LIMIT 10;
-- Find unused indexes
SELECT indexrelname, idx_scan, pg_size_pretty(pg_relation_size(indexrelid))
FROM pg_stat_user_indexes
WHERE idx_scan = 0
ORDER BY pg_relation_size(indexrelid) DESC;
アンチパターン
- 実際のクエリパターンを分析せずに、すべてのカラムにインデックスを作成する
- 必要なカラムが一部だけなのに
SELECT *を使用する EXPLAIN ANALYZEを使用してインデックスの使用状況を確認しない- 適切な型を持つ別テーブルを使うほうがよい大きな BLOB を JSONB に保存する
- コネクションプーリングを使用しない(各コネクションでサーバーのメモリを約 10MB 使用します)
- ピーク時間帯に
VACUUM FULLを実行する(テーブル全体がロックされます)
チェックリスト
- インデックスが実際のクエリパターンに合っている(
pg_stat_statementsを確認) - 複合インデックスの順序が、等価条件、ソート、範囲条件のカラムになっている
- 重要なクエリすべてで
EXPLAIN ANALYZEを実行している - 頻繁に絞り込み対象となるサブセットに部分インデックスを使用している
- PostgreSQL の前段にコネクションプーラー(PgBouncer/pgcat)を配置している
- 1,000 万行を超えるテーブルでパーティション分割を検討している
- 未使用のインデックスを特定して削除している
- クエリのパフォーマンス監視のために
pg_stat_statementsを有効にしている
コメント
Markdown 対応