過去数週間、グアバのMapMakerを使用して、理想的なキャッシュの実装を見つけようとしてきました。私の思考プロセスに従うために、こことここで私の前の 2 つの質問を参照してください。
学んだことを踏まえて、次の試みはソフト値を捨てて maximumSize と expireAfterAccess を使用することです。
ConcurrentMap<String, MyObject> cache = new MapMaker()
.maximumSize(MAXIMUM_SIZE)
.expireAfterAccess(MINUTES_TO_EXPIRY, TimeUnit.MINUTES)
.makeComputingMap(loadFunction);
どこ
Function<String, MyObject> loadFunction = new Function<String, MyObject>() {
@Override
public MyObject apply(String uidKey) {
return getFromDataBase(uidKey);
}
};
ただし、私がまだ取り組んでいる残りの 1 つの問題は、この実装では、到達可能性が高い場合でも、時間切れになるとオブジェクトが削除されることです。これにより、同じ UID を持つ複数のオブジェクトが環境内に浮かぶ可能性がありますが、これは望ましくありません (私が達成しようとしているのは、正規化として知られていると思います)。
したがって、唯一の答えは、データオブジェクトがまだメモリ内にあるかどうかを確認できるインターナーとして機能する追加のマップを用意することです。
ConcurrentMap<String, MyObject> interner = new MapMaker()
.weakValues()
.makeMap();
load 関数は次のように修正されます。
Function<String, MyObject> loadFunction = new Function<String, MyObject>() {
@Override
public MyObject apply(String uidKey) {
MyObject dataObject = interner.get(uidKey);
if (dataObject == null) {
dataObject = getFromDataBase(uidKey);
interner.put(uidKey, dataObject);
}
return dataObject;
}
};
ただし、キャッシュに 1 つではなく 2 つのマップを使用するのは効率が悪いようです。これにアプローチするより洗練された方法はありますか?一般的に、私はこれを正しい方法で行っていますか、それともキャッシュ戦略を再考する必要がありますか?