📄
概念 📚 software-design-concepts

キャッシング

キャッシュ戦略(Cache-Aside・Write-Through・Write-Back)と無効化・整合性問題の設計

キャッシングとは、高コストな計算やデータ取得の結果を一時的に保存し、同一リクエストへの応答を高速化する技術である。適切なキャッシュ戦略は読み取りスループットを大幅に改善し、バックエンドへの負荷を削減するが、「キャッシュの無効化」はコンピュータサイエンスにおける難問の一つとして知られており、整合性管理を誤るとユーザーが古いデータを参照し続けるリスクがある。

主なキャッシュ戦略として、Cache-Aside(Lazy Loading)はアプリケーションがキャッシュミス時にDBから取得してキャッシュに書き込むパターンで最も広く使われる。Read-Through はキャッシュ層がDB取得を透過的に行う。Write-Through は書き込み時にキャッシュとDBの両方を同期更新するため強い整合性を保てるがレイテンシが増す。Write-Back(Write-Behind)はキャッシュへの書き込み後に非同期でDBに反映するため書き込みスループットが高いが、キャッシュ障害時にデータ損失のリスクがある。

TTL(Time To Live)はキャッシュの有効期限を設定してエントリを自動削除する最もシンプルな無効化手法だが、TTL 中に元データが変わった場合は古いデータを返し続ける。キャッシュスタンピード(Cache Stampede)は、人気エントリの TTL が切れた瞬間に大量リクエストが同時に DB へ到達する問題で、確率的早期再計算(Probabilistic Early Recomputation)やロックによるシングルフライト(Single Flight / Request Coalescing)で対策する。分散キャッシュでは複数ノード間の一貫性が問題になり、CDN キャッシュはエッジでの無効化(Cache Purge)のタイミング制御が重要になる。

コードレビューで着目するポイント

  • キャッシュ戦略(Cache-Aside / Write-Through 等)がユースケースの整合性要件と一致しているか
  • TTL の設定値がデータの更新頻度とユーザー体験の許容レベルに基づいて決定されているか
  • キャッシュキーの設計が適切か(ユーザー・テナントなどのスコープが正しく含まれているか)
  • キャッシュスタンピードへの対策(シングルフライト・早期再計算)が実装されているか
  • キャッシュ無効化のロジックがデータ変更箇所と同期しているか(無効化漏れがないか)
  • センシティブなデータ(個人情報・認証情報)がキャッシュに保存されていないか
  • CDN キャッシュとアプリケーションキャッシュで責務と無効化方法が明確に分離されているか

典型的なアンチパターン

キャッシュキーの衝突(Cache Key Collision): テナントやユーザースコープを含めずにキャッシュキーを設計し、異なるユーザーのデータが混在する重大なセキュリティ問題。マルチテナントアプリでは必ずキーにスコープ識別子を含める。

過剰なキャッシュ(Cache Everything): 更新頻度の高いデータや、ユーザーごとに異なるデータを同じ TTL でキャッシュし、古いデータが大量に流通するパターン。キャッシュ対象とTTLはデータ特性に応じて個別に設計すべき。

キャッシュ無効化の手動管理: データ更新箇所が増えるたびに手動でキャッシュ無効化コードを追加するパターン。更新箇所の追加時に無効化漏れが発生しやすく、整合性バグの温床となる。イベント駆動の無効化や CQRSパターンとの組み合わせを検討する。

参考リソース