PHPにおけるキャッシュ無効化は、TTLを選びRedisにレスポンスを保存することではありません。これは一貫性の判断です。どの情報に遅延を許容できるか、その期間はどれほどか、元データが変化したとき何をすべきかを決定します。設計の悪いキャッシュは古い価格を表示し、取り消された権限でアクセスを許可し、すでに存在しない可用性を表示する可能性があります。反対に、保守的すぎるキャッシュはすべての負荷をデータベースへ移し、本来の目的を失います。
出発点は、各エントリを所有者、ライフサイクル、リスクが定義されたデータとして扱うことです。これにより、プロダクト、ビジネス、技術の各関係者は、潜在的に古い読み取りがいつ許容されるか、いつ信頼できる情報源から現在の状態を取得すべきかについて合意できます。
キャッシュする前にデータを分類する

頻繁に読み取られるデータがすべてキャッシュに適するわけではなく、すべてに同じ仕組みを適用できるわけでもありません。各読み取りを、変動性、古さによる影響、情報源を照会するコスト、キャッシュ障害への耐性という4つの基準で評価してください。
- 低変動・低影響: 公開カタログ、メタデータ、非機密の設定は、変更プロセスに応じて、数分または数時間のTTLを許容できることが一般的です。
- 中程度の変動性: 商品詳細、ダッシュボード集計、検索結果は、構成レコードが変わった際に無効化されるならキャッシュできます。
- 高影響: 権限、残高、上限、トランザクション状態、購入確定中の在庫、認可制御には、一貫した信頼できる情報源、または明示的で非常に厳格な鮮度戦略が必要です。
- 計算コストが高いデータ: レポートや派生サマリーは、頻繁に照会されなくてもキャッシュを正当化できる場合がありますが、それらを無効化するエンティティを宣言しなければなりません。
表示用キャッシュと判断用キャッシュを分けると有用です。カテゴリの以前の名前を数秒間表示することは許容できる場合があります。古いポリシーを使って操作を認可することは、通常は許容できません。重要な判断では、権威ある情報源を照会するか、実行前に検証できるバージョンを保存してください。
所有権、キー、鮮度契約を定義する
各エントリには運用シートが必要です。そこには、信頼できる情報源、キー、コンシューマー、最大TTL、無効化イベント、Redisが利用できない場合の動作、機能または技術上の責任者を記載します。この契約がなければキーが増殖し、変更後に何を削除すべきか誰にも分かりません。
予測可能で十分なスコープを持つキーを使用してください。たとえば、product:42は特定のエンティティを表し、tenant:8:product:42は組織間でデータが混在するのを防ぎ、dashboard:tenant:8:period:currentは派生結果を識別します。キーに秘密情報を含めたり、不安定なシリアライズを識別子として使ったりしないでください。
値の形式も統一しておくべきです。ペイロード、バージョンまたは生成日時、必要に応じて鮮度指標を保持します。コンシューマーは、キャッシュされたレスポンスがトランザクション整合性のある読み取りと同等だと仮定すべきではありません。
final class ProductCacheKey
{
public static function detail(int $tenantId, int $productId): string
{
return "tenant:{$tenantId}:product:{$productId}:v1";
}
}スキーマ接尾辞により、過去のすべてのエントリを探して削除せずに値の構造を変更できます。これはビジネスデータの無効化に代わるものではありませんが、形式の進化時にリスクを減らします。
読み取りの種類に応じて更新パターンを選ぶ
再利用可能な読み取りのためのCache-Aside
cache-asideでは、アプリケーションはまずキーを検索し、存在しなければデータベースを照会して値を構築し、TTL付きで保存します。これはシンプルで、比較的安定した読み取りに適しています。その限界は明確です。書き込み後には、誰かが影響を受けるエントリを削除または置き換えなければなりません。
無効化はトランザクションのコミット後に行う必要があります。コミット前に削除すると、別プロセスがまだ古い値でキャッシュを再構築するおそれがあります。アプリケーションがイベントを発行する場合、outboxパターンは同じトランザクション内で変更を記録し、その後に無効化命令を信頼性高く配信するのに役立ちます。
明示的な更新とバージョニング
エンティティが非常に高頻度で読み取られ、その変更が管理されている場合は、書き込みのコミット後にエントリを更新できます。これにより、次のキャッシュミスを回避できます。ただし、そのプロセスはリーダーが期待する表現と完全に同じものを生成しなければなりません。そうでなければ、無効化して再構築するほうが通常は低リスクです。
キーのバージョニングは、広範な依存関係に有用です。組織の商品リストをすべて削除する代わりに、tenant:8:products:versionをインクリメントし、リストのキーにその番号を含めます。古いリストは自動的に期限切れになります。この方法は大量削除を減らしますが、キーの増加を制御する必要があり、理解されていない依存関係を隠すために使うべきではありません。
競合と派生依存関係を制御する
典型的な競合は次のように発生します。読み取りがキャッシュミスとなり古い値を照会する。書き込みがコミットされ無効化する。最初の読み取りが完了し、古い値を再び保存するのです。機密性の高いデータでは、無効化とエンティティバージョンまたは短時間の再構築ロックを組み合わせてください。計算済みの値を書き込む前に、照会したバージョンが依然として最新かを確認します。そうでなければ結果を破棄し、再読み取りします。
分散ロックは短時間とし、有効期限を持たせ、単一のボトルネックにならないようにしなければなりません。その役割は同時再構築を減らすことであり、それだけでビジネス上の一貫性を保証することではありません。ロックを取得できない場合は、再構築された値を短時間待つか、制限付きの直接読み取りを許可する方法があります。
依存関係による無効化には棚卸しが必要です。商品変更は、その詳細、複数のリスト、検索結果、カウンター、ダッシュボードに影響する可能性があります。ロール変更は、ユーザーの有効権限や派生メニューに影響する可能性があります。これらの関係を明示的にモデル化してください。
- キーを通じて直接エンティティを無効化します。
- それに依存するコレクションと集計を無効化またはバージョニングします。
- 体験上許容される場合は、コストの高い結果を非同期で再計算します。
- ビューのクリアと信頼できる情報源の更新を混同しないでください。
関係を容易に列挙できない場合は、グローバル削除パターンで影響を受けるすべてのキーを見つけようとするより、組織、カタログ、ポリシー単位のバージョン空間のほうが通常は安全です。
TTL、ジッター、制限で情報源を保護する
TTLは安全網であり、一貫性の唯一の仕組みではありません。正しく無効化されたキーであっても期限切れにすべきです。イベント配信の失敗、デプロイエラー、孤立エントリが存在し得るためです。TTLは照会コストだけでなく、誤りのコストに応じて選択してください。
同時に作成された数千のキーが同時に期限切れにならないよう、TTLにランダムなジッターを適用してください。さらに、キーごとの再構築ロック、同時実行数制限、コンシューマーごとのクォータにより、キャッシュミスの殺到から情報源を保護します。重要でないデータでは、単一プロセスが再計算している間に少し期限切れの値を提供できます。権限または意思決定に使用する可用性については、この手法を却下するか、明示的に承認されたシナリオに限定しなければなりません。
Redis障害時の劣化動作を設計する
Redisは運用上の依存先であり、信頼できる情報源ではありません。応答しない場合、アプリケーションには定義済みの劣化モードが必要です。低コストな公開読み取りでは、タイムアウト付きでデータベースを直接照会できます。高コストな照会では、負荷制限を適用し、フィールドを減らし、一時的に利用できない状態で応答するか、アーキテクチャで想定されている場合は適切なレプリカを使用することが望まれます。
キャッシュ障害をデータベース接続の枯渇に変えてはいけません。短いタイムアウト、circuit breaker、照会予算、ルートごとのメトリクスを定義してください。重要なデータでは、鮮度を保証できない権限、残高、在庫に基づいて判断するよりも、操作を拒否するほうが望ましいです。
ヒット率だけでなく鮮度をテストし監視する
高いヒット率は、キャッシュが正しいことを証明しません。ヒット、ミス、レイテンシ、読み書きエラー、残りTTL、再構築ロック、発行済みおよび失敗した無効化に加え、データベースの照会と飽和度を計測してください。これらのシグナルは、Redisをグローバルサービスとしてだけでなく、キーの各ファミリーに関連付けてください。
テストでは、少なくとも初回読み取り、後続更新、削除、コミット後の無効化、キャッシュ停止、リーダーとライター間の競合をカバーしてください。権限を取り消した後にユーザーがアクセスを失うこと、リストが鮮度契約に従って変更を反映すること、無効化失敗がアラートまたは復旧を起動することを検証してください。
既存PHPアプリケーションのチェックリスト

- 繰り返される読み取りを列挙し、リスク、変動性、コストで分類します。
- 各データの信頼できる情報源と、許容できる古さの最大値を宣言します。
- キー、TTL、依存関係、コンシューマー、無効化イベントを文書化します。
- 無効化または更新は、確認済みのコミット後にのみ実行します。
- 同時再構築を保護し、関連する期限切れにジッターを追加します。
- データベースを過負荷にせず、Redis利用不可時の劣化モードを定義します。
- ヒット率だけでなく、鮮度と無効化を測定します。
- 所有者のいないキー、過剰なTTL、カバーされていない依存関係を定期的に見直します。
信頼できるPHPキャッシュ無効化戦略は、そのトレードオフを可視化します。何が古くなり得るのか、どの期間か、どのように修正するのか、コンポーネント障害時に何が起こるのかです。その明確さは、無差別にキャッシュを追加することより価値があります。



