現在、(。NETキャッシュを使用して)ビジネス層でエンティティとエンティティのコレクションの両方を自動的にキャッシュするアプリケーションフレームワークがあります。したがって、メソッドGetWidget(int id)はデータベースにアクセスする前にキーWidget_Id_ {0}を使用してキャッシュをチェックし、メソッドGetWidgetsByStatusId(int statusId)はWidgets_Collections_ByStatusId_ {0}を使用してキャッシュをチェックします。オブジェクトがキャッシュにない場合は、データベースから取得され、キャッシュに追加されます。
このアプローチは、読み取りシナリオでは明らかに迅速であり、包括的アプローチでは迅速に実装できますが、エンティティでCRUD操作が実行されると、大量のキャッシュキーを消去する必要があります。明らかに、追加のメソッドが追加されると、これはパフォーマンスに影響を与え、キャッシングの利点が減少します。
コレクションのキャッシングを処理する別のアプローチに興味があります。 NHibernateは、実際のエンティティではなく、コレクション内の識別子のリストをキャッシュすることを知っています。これは他の人が試したアプローチですか?長所と短所は何ですか?
特に、パフォーマンスを最適化し、ボイラープレート生成コードを介して自動的に実装できるオプションを探しています(独自のコード生成ツールがあります)。特定の状況のニーズを満たすために、毎回キャッシングを手動で行う必要があると言う人もいるでしょうが、私はほとんどの方法で自動的に行われるものを探しています。
オブジェクトキャッシュへのさまざまなアプローチを試しましたが、コレクションが実際のオブジェクトではなく参照としてキャッシュされるアプローチの利点がわかります。
例:
User GetUser(int ID);
ICollection<User> GetRecentUsers(int amount);
ICollection<User> GetActiveUsers(int amount);
void Update(User user);
この例では、3つの「Get」メソッドすべてが実際のユーザーデータをロード、キャッシュ、および返す場合、いずれかのコレクションに含まれるオブジェクトが更新されると、データは古くなります。
委任キャッシュの責任
ただし、更新を行うたびにすべてをフラッシュすると、かなり役に立たないキャッシュになります。したがって、何が古くなっているのかを知る方法が必要です。残念ながら、これを追跡することは簡単な作業ではありません。
どのデータがどこに配置されているかを追跡するのではなく、データの処理を委任し、キャッシュロジックを単一の場所に保持するスキームを作成できます。
上記の例では、GetUser(int ID)は、ユーザーオブジェクトを実際にロードおよびキャッシュできる唯一のメソッドである可能性があります。
GetRecentUsers()およびGetActiveUsers()は、ユーザーIDのリストのみをロードし、GetUser()を呼び出して、結果を返す前に実際にユーザーのコレクションを生成します。
この方法では、ユーザーオブジェクトを更新するときに、キャッシュされたオブジェクトを1つだけ無効にするだけで済みます。
GetRecentUsersが保持するIDのリストは、新しいユーザーがサイトに参加したときなどにのみ無効になります。
これにより、キャッシュを整理するためのよりクリーンなアプローチが得られます。ただし、独自のセットアップの問題が発生します。
落とし穴
コールドキャッシュから始めて、最近の10人のユーザーをロードすると、N + 1の問題が発生します。基本的に、ユーザーのリストを取得するクエリと、キャッシュにデータを追加するための10個のクエリを実行します。
これは、データと設定によっては大きな問題になる可能性があります。ただし、キャッシュの事前ウォーミングを行うか、バッチ操作を許可することによって(これらも複雑さをもたらす)、これらの問題に対する対策を講じることができます。
特効薬はありません。しかし、注意して実装した場合、委任アプローチは有用だと思います。