スレッドセーフな方法で DAO に情報をキャッシュする方法
-
20-08-2019 - |
質問
頻繁に変更されない参照データに対して DAO を実装する必要があることがよくあります。これを DAO のコレクション フィールドにキャッシュすることがあります。これにより、一度だけロードされ、必要なときに明示的に更新されます。
ただし、これにより多くの同時実行性の問題が発生します。つまり、データのロード中または更新中に別のスレッドがデータにアクセスしようとした場合はどうなるでしょうか。
明らかに、これはデータのゲッターとセッターの両方を同期させることで処理できますが、大規模な Web アプリケーションの場合、これはかなりのオーバーヘッドになります。
私がストローマンとして必要なものについて、些細な欠陥のある例を含めました。これを実装する別の方法を提案してください。
public class LocationDAOImpl implements LocationDAO {
private List<Location> locations = null;
public List<Location> getAllLocations() {
if(locations == null) {
loadAllLocations();
}
return locations;
}
詳細については、私は Hibernate と Spring を使用していますが、この要件は多くのテクノロジーに適用されます。
さらにいくつかの考え:
これはコード内でまったく処理すべきではなく、代わりに ehcache などに処理させるべきでしょうか?私が見落としているこれに関する一般的なパターンはありますか?これを実現する方法は明らかにたくさんありますが、シンプルで保守可能なパターンを私は見つけたことがありません。
前もって感謝します!
解決
独自のキャッシュ ソリューションを簡単に展開したい場合は、以下を参照してください。 これ JavaSpecialist に関する記事。この本のレビューです。 実際の Java 同時実行性 による ブライアン・ゲッツ.
を使用した基本的なスレッド セーフ キャッシュの実装について説明します。 将来の課題 そして 同時ハッシュマップ.
これを行う方法により、1 つの同時スレッドだけが長時間実行の計算をトリガーするようになります (この場合、データベースは DAO を呼び出します)。
必要に応じて、このソリューションを変更してキャッシュの有効期限を追加する必要があります。
自分でキャッシュする場合のもう 1 つの考慮事項は、ガベージ コレクションです。キャッシュに WeakHashMap を使用しない場合、GC は必要に応じてキャッシュによって使用されているメモリを解放できません。アクセス頻度の低いデータ (ただし、計算が難しいためキャッシュする価値のあるデータ) をキャッシュしている場合は、メモリ不足時に WeakHashMap を使用してガベージ コレクターを支援するとよいでしょう。
他のヒント
最も簡単かつ安全な方法は、あなたのプロジェクトににehcacheライブラリに含まれ、セットアップにそれを使用することですキャッシュ。これらの人々は、あなたが遭遇することができ、すべての問題を解決していると、彼らができるだけ速くライブラリを作っています。
場ん延、自分の参考データキャッシュ、私は一般的に使用 ReadWriteLock
削減のスレッド対立を生んでいる。ひとりひとりのaccessorsしては:
public PersistedUser getUser(String userName) throws MissingReferenceDataException {
PersistedUser ret;
rwLock.readLock().lock();
try {
ret = usersByName.get(userName);
if (ret == null) {
throw new MissingReferenceDataException(String.format("Invalid user name: %s.", userName));
}
} finally {
rwLock.readLock().unlock();
}
return ret;
}
唯一の方法を取り出し、書き込みロックが refresh()
, は、一般的な通MBean:
public void refresh() {
logger.info("Refreshing reference data.");
rwLock.writeLock().lock();
try {
usersById.clear();
usersByName.clear();
// Refresh data from underlying data source.
} finally {
rwLock.writeLock().unlock();
}
}
ちなみに、私は選択の実施のための自分のキャッシュするためには、多くの
- 私の参考データコレクションは小さいできれば根拠もお店にします。
- 私のアプリのニーズをすることから、シンプル/fast;たいし数依存関係外部ライブラリです。
- のデータにはほとんどの更新時にリフレッシュ()はかなり迅速にできます。ですから熱心にinitialise私のキャッシュでおわらじ男の例では、accessorsの必要がないほどの取り出し、書き込みロックが解除されます。
休止状態の二次キャッシュは、合理的な解決策である可能性があります。
このように、取り扱うことのできるどこに、どのようなものがあるかのsetterか、セッターのデータを同期で大きなwebアプリとして取り込むことができ、飛躍するオーバーヘッド。
していた些細な欠陥のある例だとしてstrawman.ご提案ください代替方法を実施します。
このように回すことがあるので、注意してくださのサンプルコードがあり必ず同期することにな並行処理で問題がlazy-負荷の locations
.そのアクセス用メソッドは同期化されませんし、まして
- 複数のスレッドにアクセス
loadAllLocations()
方法を同時に - 一部のスレッドが入
loadAllLocations()
後でも別のスレッドが完了した方法で、割り当てた結果がlocations
-下のJavaメモリモデルしないという保証はありませんが他のスレッドの変更は、変数なしで同期します。
利用の際にはご注意を怠積み込み/初期化を行う単純なパフォーマンス向上がで締め付けて感触をつかむことも避けねじます。
私は右のそれを得ることは非常に困難なものであるので、それは、それを自分でしないのがベストだと思います。 HibernateとSpringとEhcacheのかOSCacheのを使用すると、はるかに良いアイデアです。
のほかに、それは問題であるかもしれない、あなたのDAOはステートフルになります。あなたは春があなたのために管理し、接続、工場、またはテンプレートオブジェクトのほかに、まったくの状態を持っていないはずです。
UPDATE:あなたの参照データが大きすぎではない、と変化しない本当にない場合は、おそらく別の設計は、列挙型を作成し、完全にデータベースを省略するだろう。ノーキャッシュ、ノーHibernateは、心配ありません。おそらくoxbow_lakes'ポイントは検討する価値があります:おそらくそれは非常に単純なシステムである可能性があります。
。