技術顧問や講演の場で、論理削除について見解を聞かれる場面がよくあります。アプリケーション開発者の方にとって、身近なデータベースの疑問なんでしょうね。
しっかり言語化できてなかったので、ブログに書いておきます。
論理削除をどう考えるかは、諸派あるんだろうと思ってます。 自分の意見が正しいと言うつもりはありませんし、求めらる要件や環境で結論が変わることもあると思います。
論理削除とは(おさらい)
以下のように削除日付(deleted_at)のカラムを設けて、レコードの有効・無効を管理する手法を指します。
| id | name | deleted_at |
|---|---|---|
| 1 | aaaaaa | NULL |
| 2 | bbbbbb | 2021-01-12 10:00:00 |
論理「削除」と呼びますが、実際は、非表示フラグやレコードの状態遷移を表していたり。よくよく考えると「削除」でないパターンも含まれています。逆に、DELETE して完全にデータを削除することを「物理削除」と呼びます。
誤操作の取り消しが、論理削除を採用する代表的な目的でしょう。
誤操作の保険としてはバックアップもありますが、バックアップは、データベース全体の時間軸を戻すことを意図したものです。 特定のレコードに絞って時間軸を戻すのには適していません。
論理削除のデメリットと言われているもの
そもそも「削除」ではないものを論理削除としてしまっている
これは、論理削除自体の問題点ではないのですが、設計が甘く、削除として扱うべきでないケースを論理削除として扱ってしまい問題となるケースがあります。 例えば「退会」などです。「退会済」ユーザに対しても、なんらかの処理をするのであれば、それは「削除」とは区別すべきです。
ユニーク制約が機能不全になる
| id | deleted_at | |
|---|---|---|
| 1 | aaaa@example.com | NULL |
| 2 | aaaa@example.com | 2021-01-12 10:00:00 |
例えば、email は必ずユニークにしたいとします。論理削除を採用すると、email カラム単体でユニーク制約をつけることができません。
email に加えて、deleted_at を含めてユニーク制約を設定する必要があります。
特に、外部キー制約を使う場合、問題となります。 email に対する外部キーを持つテーブルを作ったときに、親レコードが論理削除済だとしても制約を満たしてしまいます。
バグの温床になりやすい
WHERE 句に deleted_at IS NULL を付けまくらないといけないというやつですね。deleted_at IS NULL を付け忘れると削除済みデータが見えてしまうと。
パフォーマンスへの影響
本来必要のないレコードが保存されることで、そのぶんオーバーヘッドになる。
ぼくの見解
論理削除をそれほど、強いアンチパターンだとは考えてません。他のアンチパターンと比較すると問題になる確率も低いように思います。 自分の周囲では論理削除が問題になっているケースをほとんど見たことがないです。
もちろん物理削除のほうがデータがクリーンで好ましいと思います。ただ、論理削除を撲滅するために、大きな労力を割く必要は感じないです。
パフォーマンス影響については、論理削除の有無より他の要素に左右されるケースがほとんどだと思います。
むしろ、現場では、貯め続けていると、そのうち明らかに問題になるデータ量なのに、ノーガードで運用されているケース に出くわすほうが多いです。論理削除云々を気にする以前に、データ量の見積もりと、適切なデータストアの選定をしっかりしてほしい。。。*1
*1:あと、物理削除バッチの作りが悪くて、爆発することも多い