運用と注意点
本番運用に必要な観点を一挙に。バックアップ、マイグレーション、セキュリティ、監視、コスト、選定基準。
バックアップ
- マネージド DB は自動。それでも別場所に独立コピー
- 3-2-1 ルール: 3 コピー、2 種類のメディア、1 つはオフサイト
- 世代管理(毎日 7、毎週 4、毎月 12)
- 復元演習を年 1 回
- 論理ダンプ(pg_dump)は移行に強い
- WAL アーカイブで PITR(任意時刻復元)
マイグレーション
- SQL ファイルとして履歴化(Prisma / Sqitch / Flyway / Liquibase / dbmate)
- 本番では自動適用しない: 確認プロセスを挟む
- 大規模な ALTER は段階的に(expand → migrate → contract)
CONCURRENTLYでロックを避ける- 失敗時のロールバック手順を事前準備
- ステージングで本番量の実時間計測
セキュリティ
- SQL Injection対策: プレースホルダ / ORM / プリペアドステートメント
- 最小権限の原則: アプリ用ユーザはSELECT/INSERT/UPDATE/DELETE のみ
- DDL はマイグレーション専用ユーザで
- 機密列(password / token)は絶対に SELECT で漏らさない運用
- パスワードは bcrypt / argon2 でハッシュ
- 転送はTLS 必須
- ストレージレベル暗号化(マネージドはデフォルト)
- 監査ログ(pgaudit など)
- RLS で行単位の権限(→ Supabase RLS)
多テナント設計の選び方
- 共有テーブル + tenant_id + RLS(推奨。SaaS の標準)
- スキーマ分離(中規模・分離度向上)
- DB 完全分離(高分離・高コスト・コンプライアンス)
NULL と空文字
- 「不明」は NULL、「空である」は空文字
'' - 意味を統一する(チームで決め打ち)
- NOT NULL を最初から付ける運用が後で楽
時刻の扱い
- 必ず TIMESTAMPTZ(UTC で保存)
- 表示時にタイムゾーン変換
now()も TZ 付きで保存される- サマータイムを跨ぐと TIMESTAMP(TZ 無し)は危険
SQL Injection 対策
絶対 NG
// ユーザ入力を直接埋め込み
sql.unsafe(`SELECT * FROM users WHERE email = '${email}'`)
OK(プレースホルダ)
// pg
client.query("SELECT * FROM users WHERE email = $1", [email])
// postgres.js
sql`SELECT * FROM users WHERE email = ${email}`
// Prisma(ORM 経由なら基本安全)
prisma.user.findUnique({ where: { email } })
パスワード保存
import bcrypt from "bcrypt"
// 保存時
const hash = await bcrypt.hash(password, 12)
// 検証時
const ok = await bcrypt.compare(password, user.password_hash)
- 平文パスワードを絶対に保存しない
- argon2id が現代的にはベスト(より遅く・GPU 耐性)
- ソルトは bcrypt / argon2 が自動で含む
機密列の扱い
- password / token / secret 列はSELECT で除外がデフォルト
- API レスポンスに混ぜない(型レベルで弾く)
- ログに出力しない(クエリログにも注意)
- ROW セキュリティを併用
監視
- クエリレイテンシ (p50 / p95 / p99)
- 接続数 / Lock waits
- レプリカラグ
- CPU / Memory / Disk I/O
- ディスク残量
- WAL 滞留
- スロークエリログ
- 失敗エラーレート
コスト最適化
- 不要なインデックスを削除
- 古いログ・履歴をパーティション + DROP で削除
- 分析は OLAP 系へ(Postgres を綺麗に保つ)
- 巨大 BLOB は別ストレージ(S3 / R2)
- Read Replica は本当に必要か検討(コスト 2 倍)
- マネージド DB は同性能でもプロバイダで価格倍違う
環境分離
- local: Docker / SQLite
- preview: PR ごとに独立 DB(Neon ブランチング / Supabase 別プロジェクト)
- staging: 本番同等
- production: ユーザ向け
選定の指針(2026 年時点)
個人開発 / プロトタイピング
- SQLite(Turso / Cloudflare D1)— ファイル 1 つで完結
- Supabase Free — Postgres + Auth + Storage
- Neon Free — Postgres、ブランチング
小〜中規模 SaaS
- Supabase Pro / Neon / Railway / Render Postgres
- Redis を Upstash / Redis Cloud で追加
中〜大規模
- AWS RDS / Aurora
- GCP Cloud SQL / AlloyDB
- Crunchy Bridge
- 必要に応じて分析層を追加(BigQuery / Snowflake)
超大規模
- 分散 SQL: CockroachDB / Spanner / TiDB
- Citus: Postgres ベースのシャーディング
- 専門チーム必須
移行時のポイント
- 論理ダンプは互換性が高い
- マイグレーションは事前に新環境で全て流す
- 切替時のダウンタイムを最小化(dual-write 戦略)
- 新旧並走でデータ整合性検証
- ロールバック計画を必ず用意
「DB 入門」のおすすめ進路
- SQLite + SQL を 1 ヶ月触る
- Postgres + DBeaver で GUI に慣れる
- EXPLAIN を読めるようになる
- RLS / トランザクションの実装を 1 つ作る
- Supabase / Neon で本番デプロイ
- 運用ノウハウは事故から学ぶ(小さく失敗)
関連書籍 / 学習リソース
- 「達人に学ぶ DB 設計徹底指南書」(書籍)
- 「SQL アンチパターン」(書籍)
- 「Designing Data-Intensive Applications」(書籍 / 必読)
- Use The Index, Luke! — インデックス専門
- PostgreSQL 公式 Docs
- PG Performance Wiki
本セクションの総まとめ
- RDB(特に Postgres)を主軸に置く
- 正規化 + 必要最小限の非正規化
- 主キー / FK / NOT NULL / UNIQUE / CHECK で品質を守る
- FK 列にはインデックス必須
- トランザクション短く、外部 API 呼び出しは外
- EXPLAIN ANALYZE を読めるように
- マネージド DB を活用、事故は前提に設計
最後に
DB はアプリの寿命を決める。コードは書き直せるが、データは失えない。 「測って・小さく試して・記録する」習慣を持てば、複雑な選定も現実的にこなせる。