📡
概念 #データ設計 #モニタリング #オブザーバビリティ #PostgreSQL #パフォーマンス #DDIA 📚 データ志向アプリケーション設計(DDIA)

DBモニタリングとオブザーバビリティ

本番PostgreSQLで何を監視すべきか。pg_stat_statements・スロークエリ・ロック待ちの検出、メトリクス設計とアラート閾値の考え方を理解する

定義

DBのオブザーバビリティとは、外部から観測できる出力(メトリクス・ログ・トレース)によって、DBの内部状態を推測できる度合い。問題が起きてから調べるのではなく、問題が起きる前に検出し、起きた後に原因を追える設計が目標。

監視すべき4層

1. インフラ層:    CPU・メモリ・ディスクI/O・ネットワーク
2. 接続層:        コネクション数・待機数・プール利用率
3. クエリ層:      スロークエリ・実行回数・エラー率
4. トランザクション層: ロック待ち・デッドロック・レプリケーション遅延

pg_stat_statements(クエリ統計)

最も重要な拡張機能。クエリごとの実行統計を蓄積する。

-- 有効化
CREATE EXTENSION pg_stat_statements;

-- postgresql.conf
-- shared_preload_libraries = 'pg_stat_statements'
-- pg_stat_statements.max = 10000
-- pg_stat_statements.track = all

-- 遅いクエリ上位10件(平均実行時間順)
SELECT
  LEFT(query, 100)         AS query,
  calls,
  ROUND(mean_exec_time::numeric, 2) AS avg_ms,
  ROUND(total_exec_time::numeric, 2) AS total_ms,
  ROUND(stddev_exec_time::numeric, 2) AS stddev_ms,
  rows / calls             AS avg_rows
FROM pg_stat_statements
ORDER BY mean_exec_time DESC
LIMIT 10;

-- キャッシュミス率が高いクエリ(ディスクI/Oが多い)
SELECT
  LEFT(query, 100) AS query,
  shared_blks_hit,
  shared_blks_read,
  ROUND(shared_blks_hit::numeric /
    NULLIF(shared_blks_hit + shared_blks_read, 0) * 100, 1) AS cache_hit_pct
FROM pg_stat_statements
ORDER BY shared_blks_read DESC
LIMIT 10;

-- 統計をリセット(定期的に)
SELECT pg_stat_statements_reset();

ロック待ちの検出

-- 現在ロック待ちしているクエリ
SELECT
  blocked.pid,
  blocked.query,
  blocking.pid     AS blocking_pid,
  blocking.query   AS blocking_query,
  blocked_activity.wait_event_type,
  blocked_activity.wait_event
FROM pg_stat_activity AS blocked
JOIN pg_stat_activity AS blocking
  ON blocking.pid = ANY(pg_blocking_pids(blocked.pid))
JOIN pg_stat_activity AS blocked_activity
  ON blocked_activity.pid = blocked.pid
WHERE blocked.cardinality(pg_blocking_pids(blocked.pid)) > 0;

-- 長時間実行中のクエリ(5分以上)
SELECT
  pid,
  now() - query_start AS duration,
  state,
  LEFT(query, 200) AS query
FROM pg_stat_activity
WHERE state != 'idle'
  AND query_start < NOW() - INTERVAL '5 minutes'
ORDER BY duration DESC;

-- アイドルインTxのセッション(コネクションを占有している)
SELECT pid, now() - state_change AS idle_in_tx_duration, query
FROM pg_stat_activity
WHERE state = 'idle in transaction'
  AND state_change < NOW() - INTERVAL '1 minute';

テーブル・インデックスの統計

-- シーケンシャルスキャンが多いテーブル(インデックス追加を検討)
SELECT
  schemaname,
  tablename,
  seq_scan,
  idx_scan,
  ROUND(seq_scan::numeric / NULLIF(seq_scan + idx_scan, 0) * 100, 1) AS seq_pct,
  n_live_tup AS live_rows
FROM pg_stat_user_tables
WHERE seq_scan > 100
ORDER BY seq_scan DESC;

-- 使われていないインデックス(削除候補)
SELECT
  schemaname,
  tablename,
  indexname,
  idx_scan AS times_used,
  pg_size_pretty(pg_relation_size(indexrelid)) AS index_size
FROM pg_stat_user_indexes
WHERE idx_scan < 50
  AND indexrelid NOT IN (SELECT conindid FROM pg_constraint)  -- 制約インデックスは除外
ORDER BY pg_relation_size(indexrelid) DESC;

-- テーブルのブロート(デッドタプルが多いテーブル)
SELECT
  tablename,
  n_live_tup,
  n_dead_tup,
  ROUND(n_dead_tup::numeric / NULLIF(n_live_tup + n_dead_tup, 0) * 100, 1) AS dead_pct,
  last_autovacuum
FROM pg_stat_user_tables
WHERE n_dead_tup > 1000
ORDER BY dead_pct DESC;

レプリケーション監視

-- プライマリ側: スタンバイの遅延
SELECT
  client_addr,
  state,
  sent_lsn,
  write_lsn,
  flush_lsn,
  replay_lsn,
  pg_wal_lsn_diff(sent_lsn, replay_lsn) AS replay_lag_bytes,
  write_lag,
  flush_lag,
  replay_lag
FROM pg_stat_replication;

-- スタンバイ側: プライマリとの遅延
SELECT
  now() - pg_last_xact_replay_timestamp() AS replication_delay;

キャッシュヒット率

-- DBレベルのキャッシュヒット率(95%以上が目標)
SELECT
  datname,
  blks_hit,
  blks_read,
  ROUND(blks_hit::numeric / NULLIF(blks_hit + blks_read, 0) * 100, 2) AS cache_hit_pct
FROM pg_stat_database
WHERE datname = current_database();

-- テーブルレベルのキャッシュヒット率
SELECT
  tablename,
  heap_blks_hit,
  heap_blks_read,
  ROUND(heap_blks_hit::numeric / NULLIF(heap_blks_hit + heap_blks_read, 0) * 100, 1) AS cache_pct
FROM pg_statio_user_tables
ORDER BY heap_blks_read DESC
LIMIT 20;

Prometheusでのメトリクス収集

# postgres_exporter(prometheus/postgres_exporter)の設定
# docker-compose.yml
services:
  postgres-exporter:
    image: prometheuscommunity/postgres-exporter
    environment:
      DATA_SOURCE_NAME: "postgresql://monitor_user:pass@postgres:5432/mydb?sslmode=disable"
    ports:
      - "9187:9187"

主要なメトリクス

メトリクス説明アラート閾値(目安)
pg_stat_activity_countアクティブなコネクション数max_connections × 0.8
pg_stat_replication_lag_bytesレプリケーション遅延> 100MB
pg_database_blks_hit_ratioキャッシュヒット率< 0.95
pg_stat_bgwriter_buffers_cleanチェックポイント頻度急増
pg_locks_countロック数急増

ログ設定(postgresql.conf)

# スロークエリログ(100ms以上)
log_min_duration_statement = 100

# ロック待ちタイムアウトのログ
deadlock_timeout = 1s
log_lock_waits = on

# 接続・切断のログ
log_connections = off   # 高トラフィックでは無効に
log_disconnections = off

# チェックポイントのログ
log_checkpoints = on

# 一時ファイルのログ(work_memが不足しているサイン)
log_temp_files = 10MB   # 10MB以上の一時ファイルをログ

監視ダッシュボードの設計

Grafanaダッシュボードの推奨パネル:

Row 1 - 全体の健全性:
  ✅ キャッシュヒット率(>95%)
  ✅ アクティブコネクション数
  ✅ トランザクション数/秒
  ✅ コミット/ロールバック比率

Row 2 - パフォーマンス:
  ✅ クエリ実行時間p50/p95/p99
  ✅ ロック待ち数
  ✅ デッドロック数/分

Row 3 - ストレージ:
  ✅ ディスク使用量
  ✅ WALの生成速度
  ✅ ブロートの多いテーブル

Row 4 - レプリケーション:
  ✅ レプリカ遅延(バイト/秒)
  ✅ WALラグ

関連概念

出典・参考文献

  • PostgreSQL Documentation, “Monitoring Database Activity” — postgresql.org/docs/current/monitoring.html
  • Datadog, “Key metrics for PostgreSQL monitoring” — datadoghq.com
  • Martin Kleppmann, Designing Data-Intensive Applications (2017) Chapter 1(信頼性)
  1. 1. 🗄️データ志向アプリケーション設計:概要
  2. 2. 🧩データモデルとクエリ言語
  3. 3. 💾ストレージエンジンとインデックス
  4. 4. 🔁レプリケーション
  5. 5. 🍕パーティショニング(シャーディング)
  6. 6. 🔒トランザクションとACID
  7. 7. 分散システムの本質的な問題
  8. 8. 🤝一貫性と分散合意
  9. 9. 📦バッチ処理
  10. 10. 🌊ストリーム処理
  11. 11. 📋エンコーディングとスキーマ進化
  12. 12. 🔗Sagaパターンと分散トランザクション
  13. 13. 🏗️データシステムの統合設計
  14. 14. 📸MVCC(多版型同時実行制御)
  15. 15. 📊列指向ストレージとOLAP設計
  16. 16. 🕰️ベクタークロックと因果順序
  17. 17. 🔀CRDT(競合なし複製データ型)
  18. 18. 🔍クエリオプティマイザーと実行計画
  19. 19. キャッシュ戦略とRedis設計
  20. 20. 🔎全文検索と転置インデックス
  21. 21. 🌐NewSQL(分散ACIDデータベース)
  22. 22. 📝WALと論理レプリケーション
  23. 23. 🔌コネクションプーリング
  24. 24. 🚧ゼロダウンタイムマイグレーション
  25. 25. 🆔分散ID生成
  26. 26. 🔄N+1問題とDataLoaderパターン
  27. 27. 📈タイムシリーズDB
  28. 28. 🛡️Row Level Security(行レベルセキュリティ)
  29. 29. 📤Outboxパターン(トランザクショナルアウトボックス)
  30. 30. 💾DBバックアップとPITR
  31. 31. ⚠️データベース設計アンチパターン
  32. 32. 🕸️グラフDB深掘り
  33. 33. 🔋バックプレッシャーとサーキットブレーカー
  34. 34. 🔵コンシステントハッシング
  35. 35. 📋マテリアライズドビュー
  36. 36. 📡DBモニタリングとオブザーバビリティ
  37. 37. 🔐データプライバシーとCrypto-Shredding
  38. 38. ✂️垂直分割(Vertical Partitioning)
  39. 39. 🪟ウィンドウ関数
  40. 40. 🧲ベクトルDBとpgvector
  41. 41. 🔧dbtとデータ変換パイプライン
  42. 42. 📬ジョブキューの設計
  43. 43. 📐正規化理論(1NF〜BCNF)
  44. 44. ☁️クラウドDBサービスの設計思想と選択基準
  45. 45. 🗺️地理空間データとPostGIS
  46. 46. 🔑DBセキュリティと権限管理
  47. 47. 🏔️Lakehouse(Apache Iceberg / Delta Lake)
  48. 48. 📜データコントラクト
  49. 49. 🔭OpenTelemetryとDBトレーシング

出典: Martin Kleppmann, 'Designing Data-Intensive Applications' (2017) Chapter 1