Выключение и включение в течение последних нескольких недель я пытался найти свою идеальную реализацию кеша, используя guava MapMaker. Смотрите мои предыдущие два вопроса здесь и здесь, чтобы следовать моему мыслительному процессу.
Взяв то, что я узнал, моя следующая попытка будет опускать мягкие значения в пользу 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);
}
};
Тем не менее, одна из оставшихся проблем, с которыми я все еще сталкиваюсь, заключается в том, что эта реализация будет вытеснять объекты, даже если они сильно достижимы, как только их время закончится. Это может привести к тому, что несколько объектов с одним и тем же UID будут перемещаться в среде, чего я не хочу (я считаю, что то, что я пытаюсь достичь, называется канонизацией).
Итак, насколько я могу судить, единственным ответом является наличие дополнительной карты, которая функционирует как интерсер, которую я могу проверить, чтобы увидеть, все еще в памяти объект данных:
ConcurrentMap<String, MyObject> interner = new MapMaker()
.weakValues()
.makeMap();
и функция загрузки будет пересмотрена:
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;
}
};
Однако использование двух карт вместо одного для кэша кажется неэффективным. Есть ли более сложный способ приблизиться к этому? В общем, я собираюсь сделать это правильно, или я должен переосмыслить свою стратегию кэширования?