ブログ一覧

削除したデータが戻ってくる:キャッシュ・検索・バックアップまで消す設計

· 約8分で読めます

退会処理でDBを消しても、コピーとバックアップは残り得ます。TTLの期限判定、削除イベントの再送、復元後の再削除、大量削除の進め方を、4件のTipsから解説します。

削除したデータが戻るのは、コピーの範囲が違うから

退会した人のプロフィールが検索に残り、障害復旧後に再び表示される。対策は削除APIのSQLだけでは決まりません。この記事は、架空の会員サービスで削除要求の受付から、キャッシュ・検索・分析・外部連携・バックアップまで追う運用設計です。法的な消去期限の解説ではなく、合意した削除範囲を実装と検証へ落とします。

独自の削除フロー。配信しただけで完了にしない

認証・認可した削除要求を記録 → 正本の利用を停止し削除イベントを永続ストレージへ保存 → コピーごとに削除を反映 → 保存先の結果と残存データを確認 → 未完了を再試行・照合して完了を記録

保存先削除の方法の例完了確認
キャッシュ該当キーを無効化し、古い読取からの再投入を防ぐ削除後の取得と古い更新の再送を試す
検索・分析削除イベントや対象IDで反映IDによる照合と検索結果への反映を確認
外部連携相手の削除APIと状態照会を使う受付と処理完了を区別する
バックアップ隔離・保持期間・復元後の再適用を設計復元訓練で削除済みデータを再表示しない

TTLの期限と、読めなくする時刻は別に設計する

DynamoDBの期限切れ項目は削除前なら読み取りに現れ得ます。QueryやScanの期限フィルタに加え、直接取得やキャッシュなど、すべての利用経路で有効性を判定します。このケースではnow >= expires_atなら無効とし、物理削除の完了を待ちません。期限を短くするだけでは、本来の有効期間と非同期削除の問題を混ぜてしまいます。

期限判定の疑似コード。時刻の形式と許容する時計誤差はサービスで定義する
item = authorized_read(subject_id)
if item is missing or now >= item.expires_at:
    deny_use()
else:
    continue_with_authorized_operation(item)

利用停止、オンラインのコピーからの削除、バックアップの保持終了を別の状態として記録します。「削除済み」という1つの表示で隠さず、どこまで終わったかを説明できる契約にします。保持期間は製品ごとの仕様・保存先・復旧要件に合わせ、単一のTTL設定から全コピーの消去時刻を推定しません。

削除イベントは再送・順序逆転・失敗まで扱う

正本を消した直後に処理が落ちても削除の出来事を失わないよう、OutboxやCDCなどで接続します。受信側は削除IDと対象の世代を使って同じ要求を安全に再処理し、古い更新を拒否します。削除したIDを新規会員へ再利用すると古い削除が新データを消すため、IDを再利用しないか、世代を識別します。

DebeziumのPostgreSQLコネクターでは、行の削除イベントとKafkaログ圧縮向けのnull値のトゥームストーンを区別します。「nullを見たら消す」という独自解釈を先に作らず、配送先の形式と設定に合わせます。配信成功だけでなく保存先での削除結果を記録し、期限を超える未完了を通知します。

バックアップからの復元は、削除記録の再適用まで含む

1月1日のバックアップを1月10日に戻すと、その間に削除したデータは戻り得ます。独自例で120人が退会したなら、その対象と世代を別の記録から再適用します。削除記録は復元するDBだけに置かず、復元で巻き戻らない場所で保護します。保持期間は復元可能な最古の時点、記録の欠損検知、再送可能期間に合わせます。

  1. 復元先を外部から隔離し、通知・同期・検索公開などの自動処理を止める。
  2. 復元時点を特定し、以後の削除記録を揃え、完全性を確認する。
  3. 正本へ削除を再適用し、必要な派生コピーも削除・再構築する。
  4. 削除済みの識別子が再表示されないことを検証してから公開する。

Google Cloudの資料は、稼働系とバックアップで削除工程が異なることを説明しています。これは自社アプリのすべてのコピーを自動で処理する保証ではありません。削除記録自体にも識別子が残るため、消した本文は保存せず、用途・アクセス権・保持を絞ります。

大量削除は小分けにし、終わった範囲を記録する

大量のDELETEを1トランザクションで流すと、ロックやログ量、レプリカの遅延が問題になります。対象IDを固定し、小さな範囲でコミットして、負荷や遅延に応じて一時停止します。観測に遅れがあるため、停止しきい値は遅延の上限保証にはなりません。Percona pt-archiverはMySQL系の漸進的な処理例で、他DBへそのまま使う手順ではありません。

独自の件数計算として、300万件を1バッチ5,000件で処理するなら600バッチです。1バッチの実時間や待機は別に測り、件数だけから終了時間は約束しません。対象を先へ進めた後で古い対象が混入しないよう、進捗記録の保存と同時更新への対応も決めます。

削除のテストは復元・再送・一部停止を含める

  1. コピーを1つ停止して退会処理を実行し、未完了が追跡され、復旧後に再処理できることを確認する。
  2. 古い更新を削除後に再送し、プロフィールが復活しないことを確認する。
  3. 期限ぴったりの時刻と物理削除前に、直接取得・一覧・キャッシュの利用が拒否されることを確認する。
  4. 削除前のバックアップを隔離復元し、削除記録の再適用前は公開されず、再適用後は検索を含めて戻らないことを確かめる。

参考にした一次資料

以下の公式資料は2026年10月27日に確認しました。数値例とシナリオは独自の仮定です。設定例は説明用で、実サービスでの実行や性能測定は行っていません。

この記事で深掘りしたTips