Day26 前半のゴール
「“あとからちゃんと追えるDB”とは何かを理解する」
ここからは「ログ設計」です。
パフォーマンスやAPI連携が“攻め”だとしたら、ログは“守りと証拠”の領域です。
今日の前半のゴールはこうです。
監査ログと履歴管理の違いを言葉で説明できる
「誰が・いつ・何をしたか」を残す重要性を理解する
アプリログとDBの中に持つ履歴テーブルの役割の違いをイメージできる
まだSQLを書く前の段階として、「何を残すべきか」を丁寧に言語化していきます。
監査ログと履歴管理は何が違うのか
「“責任を追うログ”と“状態の変化を追うログ”」
まず、よく混ざる二つの言葉を分けます。
監査ログ(Audit Log)
履歴管理(History / Versioning)
どちらも「過去の情報を残す」ですが、目的が違います。
監査ログは、
誰が
いつ
どんな操作をしたか
を追うためのログです。
セキュリティ・コンプライアンス寄りの世界です。
履歴管理は、
あるデータが
いつ
どのように変わってきたか
を追うための仕組みです。
ビジネスロジック・業務要件寄りの世界です。
例えば、ユーザー情報の変更を考えます。
監査ログの視点だと、
「管理者Aが 2025-05-10 10:00 に ユーザー123 のメールアドレスを変更した」
という事実を残したい。
履歴管理の視点だと、
「ユーザー123のメールアドレスは、
2024-01-01〜2025-05-10 までは X、
それ以降は Y」
という“状態の変化”を残したい。
この二つは、似ているようで目的が違うので、
設計も少し変わってきます。
なぜ監査ログが必要なのか
「“何が起きたか”だけでなく“誰がやったか”を残す」
監査ログは、特にセキュリティ・法令対応の観点で重要です。
不正アクセスがあったとき
内部の人間が不正な操作をした疑いがあるとき
ユーザーから「いつ誰がこの情報を変えたのか」と問われたとき
こういう場面で、「ログがない」は致命的です。
監査ログで最低限押さえたいのは、次のような情報です。
いつ(日時)
誰が(ユーザーID・IPアドレスなど)
どの機能で(APIエンドポイント・画面名など)
どの対象に対して(対象のIDなど)
何をしたか(作成・更新・削除・ログイン成功/失敗など)
例えば、ユーザー情報変更の監査ログなら、
こんなイメージになります。
「2025-05-10 10:00、admin_user(管理者ID=1) が、ユーザーID=123 のメールアドレスを変更した」
ここでは、「値がどう変わったか」よりも、
「誰がどの対象に対して操作したか」が主役です。
なぜ履歴管理が必要なのか
「“今だけ”でなく“昔どうだったか”を業務で使う」
一方、履歴管理は、業務上の理由で必要になります。
料金プランが変わった
住所が変わった
商品価格が変わった
こういうときに、
「今どうなっているか」だけでなく、
「当時どうだったか」が必要になる場面が多いです。
典型的な例は請求・課金です。
請求書を出すときに、
「今の価格」で計算してはいけない
「契約当時の価格」で計算しないとおかしい
というケースは山ほどあります。
Day23 のEC設計でやった、
order_items.unit_price に「注文時点の価格」をコピーしておく
というのも、履歴管理の一種です。
履歴管理で押さえたいのは、
いつからいつまで、その値が有効だったか
どのタイミングで、どの値に変わったか
という“時間軸”です。
アプリログとDB内の履歴テーブルの違い
「“テキストで流すログ”と“SQLで集計できる履歴”」
ログというと、まず思い浮かぶのは「アプリのログファイル」かもしれません。
アプリログ(テキストログ)は、
ファイルやログ基盤(例:stdout → 集約システム)に
テキストとして書き出す
というスタイルが多いです。
例:
2025-05-10T10:00:00Z INFO user_id=1 action=update_user target_id=123 field=email
これは、人間が後から読む・全文検索するには便利ですが、
SQLで集計したり、JOINしたりするのは苦手です。
一方、DB内の履歴テーブルは、
テーブルとして設計する
SQLで検索・集計・JOINできる
という強みがあります。
例えば、ユーザー情報の履歴テーブルを作るとしたら、
こんなイメージです。
CREATE TABLE user_email_history (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
user_id BIGINT NOT NULL,
email VARCHAR(255) NOT NULL,
valid_from DATETIME NOT NULL,
valid_to DATETIME NULL
);
SQLこれなら、
「2024年1月1日時点でのメールアドレスを知りたい」
「メールアドレスの変更回数が多いユーザーを探したい」
といったクエリを、SQLで書けます。
つまり、
アプリログ
→ 何かあったときに“証拠として読む”・時系列で追う
DB内の履歴テーブル
→ 業務ロジックの一部として“普通に参照・集計する”
という役割分担になります。
どちらか一方ではなく、
両方を目的に応じて使い分ける、という感覚が大事です。
まずは「何をログに残すか」を言葉で決める
「“全部ログる”ではなく“目的から逆算する”」
ログ設計で一番やってはいけないのは、
とりあえず全部ログに出しておけば安心でしょ
という発想です。
ログは増えれば増えるほど、
保存コストが上がる
ノイズが増えて本当に必要な情報が埋もれる
個人情報の漏えいリスクが上がる
というデメリットも増えます。
だからこそ、先に「目的」を決めます。
不正アクセスや不正操作を後から追えるようにしたい
請求やトラブル対応のために、過去の状態を再現したい
システム障害の原因を調べられるようにしたい
目的が決まれば、「何を残すか」が見えてきます。
例えば、「監査ログ」を目的にするなら、
ログイン成功・失敗
重要なデータの作成・更新・削除
権限変更
などが対象になります。
「履歴管理」を目的にするなら、
料金プラン
住所・連絡先
商品価格・割引条件
など、「過去の状態が業務に効いてくるデータ」が対象になります。
Day26 前半では、
ここまでを“日本語で”整理できれば十分です。
後半で、
監査ログ用のテーブル設計の例
履歴テーブルの典型パターン(有効期間・バージョン番号)
個人情報をログに書くときの注意点
などを、具体的なSQLと一緒に見ていきます。
Day26 前半のまとめ
ログ設計を考えるときは、まず「監査ログ」と「履歴管理」を分けて捉え、監査ログは「誰が・いつ・どの対象に・どんな操作をしたか」を後から追うための“責任のログ”、履歴管理は「あるデータがいつ・どのように変わってきたか」を業務上再現するための“状態のログ”だと理解することが出発点になる。
さらに、アプリのテキストログは「人間やログ基盤が時系列で読む・検索する」用途に向き、DB内の履歴テーブルは「SQLで参照・集計・JOINする」用途に向くという役割の違いを押さえたうえで、「不正追跡」「過去状態の再現」「障害解析」といった目的から逆算して“何を・どの粒度で・どこに残すか”を言葉で決めていくことが、Day26 後半で具体的なテーブル設計や実装パターンに進むための土台になる。
